Facing a syntactic problem in Directed Acyclic Graph Python - python

Here I'm using DAG to solve Job scheduling problem.
from collections import defaultdict
class JobGraph:
def __init__(self):
self.graph = defaultdict(list)
self.indegree = defaultdict(int)
self.visited = set()
def addEdge(u, v):
self.graph(u).append(v)
try:
self.indegree[v] += 1
except:
self.indegree[v] = 1
def topologicalSort(jobs, deps, queue = [], order = [], flag = 0):
# Write your code here.
if flag == 0:
g = JobGraph()
for dep in deps:
g.addEgde(dep[0], dep[1])
for job in jobs:
if g.indegree[job] == 0:
queue.append(job)
order.append(queue[0])
root = queue[0]
del queue[0]
for neighbour in self.graph[root]:
g.indegree[neighbour] -= 1
if g.indegree[neighbour] == 0 and neighbour not in g.visited:
queue.append(neighbour)
if len(queue) == 0:
return order
else:
topologicalSort(jobs, deps, queue, order, 1)
But the error I'm getting is
'JobGraph' object has no attribute 'addEgde'
Traceback (most recent call last):
File "/tester/json_wrapper.py", line 8, in run
actual = program.topologicalSort(inputs["jobs"][:], aelib.deepCopy(inputs["deps"]))
File "/tester/program.py", line 20, in topologicalSort
g.addEgde(dep[0], dep[1])
AttributeError: 'JobGraph' object has no attribute 'addEgde'
I know it's a syntactic issue I just don't know how to solve it
Input example
jobs = [1,2,3,4]
deps = [[1,2], [1,3], [3,2], [4,2], [4,3]]

Your code seems to have other problems, but the one that's causing this error is simple and obvious. You define a method named addEdge like this:
def addEdge(u, v):
but then you are calling a method named addEgde, like this:
g.addEgde(dep[0], dep[1])
You simply have a typo. addEdge != addEgde. You've reversed the g and the d.
BTW, the signature of that method should probably be:
def addEdge(self, u, v):

Related

This Python script returns "KeyError: '6'" and I don't know why

This script is the answer to an assignment I have. The problem arises below the section I have commented as "Task 3".
My code works perfectly fine, or at least seems to do so, as it prints out the correct nodes in the graph. However, for some reason, I get "KeyError: '6'", and I don't understand why.
Here's my entire script:
# Task 1
# The answer is D, All of the above
# Task 2
def binary_tree(r):
return [r, [], []]
def get_left_child(root):
return root[1]
def get_right_child(root):
return root[2]
def insert_left_child(root, new_branch):
t = root.pop(1)
if len(t) > 1:
root.insert(1, [new_branch, t, []])
else:
root.insert(1, [new_branch, [], []])
return root
def insert_right_child(root, new_branch):
t = root.pop(2)
if len(t) > 1:
root.insert(2, [new_branch, [], t])
else:
root.insert(2, [new_branch, [], []])
return root
my_tree = binary_tree('a')
insert_left_child(my_tree, 'b')
insert_right_child(my_tree, 'c')
insert_right_child(get_right_child(my_tree), 'd')
insert_left_child(get_right_child(get_right_child(my_tree)), 'e')
print(my_tree, "\nThe answer is C")
# Task 3
class Graph:
graph = dict()
def add_edge(self, node, neighbour):
if node not in self.graph:
self.graph[node] = [neighbour]
else:
self.graph[node].append(neighbour)
def print_graph(self):
print(self.graph)
def breadth_first_search(self, node):
searched = []
search_queue = [node]
while search_queue:
searched.append(node)
node = search_queue.pop(0)
print("[", node, end=" ], ")
for neighbour in self.graph[node]:
if neighbour not in searched:
searched.append(neighbour)
search_queue.append(neighbour)
def build_my_graph2():
my_graph = Graph()
my_graph.add_edge("1", "2")
my_graph.add_edge("2", "3")
my_graph.add_edge("3", "5")
my_graph.add_edge("4", "5")
my_graph.add_edge("5", "6")
my_graph.breadth_first_search("1")
build_my_graph2()
You get a KeyError when you call for a key that is not in the dictionary. Based on your add_edge function, it looks like you create a key for 1, 2, 3, 4, 5, but you add a value only for 6.
Here you are requesting a value for key 6, but 6 in itself is not a key.
for neighbour in self.graph[node]:
Python raises a KeyError whenever a dict() object is requested (using the format a = mydict[key]) and the key is not in the dictionary.
Feel free to read more about KeyError exceptions in Python here: https://realpython.com/python-keyerror/
Which line of code throws the error?

'str' object has no attribute / error calling a dictionary

I have a class with a function that adds an edge to the graph, according to the input given. For example: if the input is add James Larry 1, an edge will be added between James and Larry with the weight (intimacy level) 1. The graph is a dictionary of sets, so, the keys are the node and the values (sets) are the edges.
So, this function has as parameters: the source, the destination and the weight. The class is represented below:
class DiGraph(object):
# Create an empty graph.
def __init__(self):
## A dictionary that stores an entry of a node, as the key, and a set of outgoing edges
# (destination node, weight) from the node, as its value.
self.graph = {}
## Total number of edges in the graph.
self.__numEdges = 0
## The largest edge distance.
# self.__infinity = sys.maxint
self.__infinity = sys.maxsize
## Holds the path from a source node to a given node.
self.__pathToNode = None
## Accumulated distance from source to a node.
self.__dist = None
### (...)
def addEdge(self, src, dst, c=1):
if ( src == None or dst == None or c <= 0 or src == dst ):
return False
# the edge set of src
eSet = self.graph.get(src)
e = Edge(dst,c) # new edge
if eSet == None:
# no edge starting at src, so create a new edge set
eSet = set()
self.__numEdges += 1
else:
ed = self.getEdge(src,dst)
if ( ed != None ):
ed.setCost(c)
return True
else:
self.__numEdges += 1
eSet.add(e) # a set does not have duplicates
self.graph[src] = eSet
if not self.hasVertex(dst):
self.addVertex(dst)
return True
I am trying to implement this code:
import DiGraph
#Create an empty graph
def main():
aGraph = {}
f = open("infile.txt")
contents = f.read()
lines = contents.splitlines()
word = []
for line in lines:
word.append(line.split())
for i in range(len(word)):
if word[i][0] == 'add':
aGraph = DiGraph.DiGraph.addEdge(word[i][1], word[i][2], int(word[i][3]))
return aGraph
grafo = main()
And the first line of the file is: add James Larry 1
This error is being showed to me when I try to run this code:
Traceback (most recent call last):
File "C:/.../SocialGraph.py", line 24, in
grafo = main()
File "C:/.../SocialGraph.py", line 20, in main
aGraph = DiGraph.DiGraph.addEdge(word[i][1], word[i][2], int(word[i][3]))
File "C:...\DiGraph.py", line 156, in addEdge
eSet = self.graph.get(src)
AttributeError: 'str' object has no attribute 'graph'
What can I do to correct this?
DiGraph.DiGraph.addEdge(word[i][1],
You call this as a static method. 'word[i][1]' becomes 'self'.
You implement addEdge as an instance method so that you have to create an instance to use it. Like so:
# create DiGraph instance
diGraph = DiGraph.DiGraph()
for i in range(len(word)):
if word[i][0] == 'add':
aGraph = diGraph.addEdge(word[i][1], word[i][2], int(word[i][3]))

Key k returned by dict.keys() causes KeyError when doing dict[k]: KeyError on existing key

The following code
for k in list(g_score.keys()):
print(g_score[k])
returns a KeyError for me:
Traceback (most recent call last):
File "./np.py", line 134, in <module>
main()
File "./np.py", line 131, in main
NPuzzle(n).solve()
File "./np.py", line 116, in solve
print(g_score[k])
KeyError: 3
I don't understand how this is possible when print(list(g_score.keys())) is [4, 3, 7]. 3 is clearly in the dict.
For context, I'm trying to implement an A* search for the N-Puzzle problem (and I'm not even sure if the A* is implemented correctly since I can't get past this error), and have the following State class and solve function:
class State:
def __init__(self, blank_idx, puzzle, g=0, h=0):
self.blank_idx = blank_idx
self.puzzle = puzzle
self.f = g + h
def __eq__(self, other):
return self.puzzle == other.puzzle
def __hash__(self):
return self.f + self.blank_idx
def __lt__(self, other):
return self.f < other.f
def __repr__(self):
return str(self.f)
...
class NPuzzle:
# ...other stuff
def solve(self):
start_state = State(
self.puzzle.index(' '),
self.puzzle,
0,
self.cost(self.puzzle)
)
g_score = {start_state: 0}
open_set = [start_state]
path = {}
while open_set:
state = open_set[0]
if state.puzzle == self.goal_state:
break
heappop(open_set)
for next_state in self.neighbors(state):
g = g_score[state] + 1
if next_state in g_score and g >= g_score[next_state]:
continue
path[next_state] = state
g_score[next_state] = g
next_state.f = g + self.cost(next_state.puzzle)
heappush(open_set, next_state)
and I first encountered the error on the line where I have:
g = g_score[state] + 1
I'm not sure really why this KeyError is occurring, but I'm assuming maybe it has to do with my custom __hash()__ function.
Ok so turns out the issue was that I was immediately changing the properties of the State instance that the hash function depended on...oops:
My __hash()__ function for State is:
return self.f + self.blank_idx
and the way I was storing State in g_score is below:
g_score[next_state] = g
next_state.f = g + self.cost(next_state.puzzle)
Turns out the above breaks everything since it uses next_state.f to put next_state into g_score, but then immediately on the next line I mutate next_state.f
Switching the order of the two statements like so:
next_state.f = g + self.cost(next_state.puzzle)
g_score[next_state] = g
fixes my issue.

Single Linked List search in Python

I want to search a value/character in a linked list and return the number of times the value/character is in the linked list. Also would it be easier if I just used recursion instead of tail recursion?
class MyList():
__slots__=('head','size')
class Empty():
__slots__=()
class NonEmpty():
__slots__=('data','next')
def mkMyList():
lst = MyList()
lst.head = mkEmpty()
lst.size = 0
return lst
def mkEmpty():
return Empty()
def mkNonEmpty(data,lst):
node = NonEmpty()
node.data = data
node.next = lst
return node
def count(l, value, c = 0):
l = mkMyList()
if l.head != value:
l.head = l.head.next
if l.head == value:
return count(l.head.next, value, c + 1)
if l.size == 0:
return c
When I try to test it, I get this:
count(s,'s',c= 0)
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
count(s,'s',c= 0)
File "C:\Users\Qasim\Desktop\Linked Lists.py", line 30, in count
l.head = l.head.next
AttributeError: 'Empty' object has no attribute 'next'
\
Rather than use recursion, I would use the iterator pattern. Here is one way to do it in the context of your problem:
class LinkedList(object):
class Node(object):
__slots__ = ('prev', 'next', 'value')
def __init__(self, prev=None, next=None, value=None):
self.prev = prev
self.next = next
self.value = value
def __init__(self, iterable=[]):
self.head = LinkedList.Node() # dummy node
self.tail = self.head
self.size = 0
for item in iterable:
self.append(item)
def __iter__(self):
current = self.head
while True:
if current.next is not None:
current = current.next
yield current.value
else:
raise StopIteration
def append(self, value):
self.tail.next = LinkedList.Node(prev=self.tail, value=value)
self.tail = self.tail.next
self.size += 1
def pop(self):
if self.size > 0:
value = self.tail.value
self.tail = self.tail.prev
self.tail.next = None
self.size -= 1
return value
else:
raise IndexError('pop from empty list')
def count(self, value):
cumsum = 0
for item in self:
if item == value:
cumsum += 1
return cumsum
By my defining a Python special method __iter__, one can sequentially access the elements of a LinkedList in the following manner:
l = LinkedList([1, 2, 3, 3, 3, 4, 5])
for value in l:
print(value)
which then makes the desired method count straight-forward to implement.
Note that I have used the Python generator syntax to implement __iter__, you can read about generators and the yield statement here.
Tracing your code:
l = mkMyList() # => head = Empty()
if l.head != value: # True since head is Empty()
l.head = l.head.next # Empty does not have a ".next" attribute
This is what the Traceback is telling you.
EDIT: Two more things: (1) I'm not sure why count is even calling mkMyList when it seems your intent is to pass it the list, l, in the function args. (2) I'm guessing you want to put the size-check if statement at the top of this function:
if l.size == 0:
return c
The issue I see is that the list in count is never initialized properly. In mkMyList(), the head element is set to and Empty, which has no next attribute. In count(), you only use mkMyList(). This means that l.head is an Empty, and there's no way it could have a next attribute. To fix this, I would recommend instantiating the list l using the given input.
With regards to the question about recursion: no, there is very little difference in terms of composing a tail recursive function versus a regularly recursive function.

Hopcroft–Karp algorithm in Python

I am trying to implement the Hopcroft Karp algorithm in Python using networkx as graph representation.
Currently I am as far as this:
#Algorithms for bipartite graphs
import networkx as nx
import collections
class HopcroftKarp(object):
INFINITY = -1
def __init__(self, G):
self.G = G
def match(self):
self.N1, self.N2 = self.partition()
self.pair = {}
self.dist = {}
self.q = collections.deque()
#init
for v in self.G:
self.pair[v] = None
self.dist[v] = HopcroftKarp.INFINITY
matching = 0
while self.bfs():
for v in self.N1:
if self.pair[v] and self.dfs(v):
matching = matching + 1
return matching
def dfs(self, v):
if v != None:
for u in self.G.neighbors_iter(v):
if self.dist[ self.pair[u] ] == self.dist[v] + 1 and self.dfs(self.pair[u]):
self.pair[u] = v
self.pair[v] = u
return True
self.dist[v] = HopcroftKarp.INFINITY
return False
return True
def bfs(self):
for v in self.N1:
if self.pair[v] == None:
self.dist[v] = 0
self.q.append(v)
else:
self.dist[v] = HopcroftKarp.INFINITY
self.dist[None] = HopcroftKarp.INFINITY
while len(self.q) > 0:
v = self.q.pop()
if v != None:
for u in self.G.neighbors_iter(v):
if self.dist[ self.pair[u] ] == HopcroftKarp.INFINITY:
self.dist[ self.pair[u] ] = self.dist[v] + 1
self.q.append(self.pair[u])
return self.dist[None] != HopcroftKarp.INFINITY
def partition(self):
return nx.bipartite_sets(self.G)
The algorithm is taken from http://en.wikipedia.org/wiki/Hopcroft%E2%80%93Karp_algorithm
However it does not work. I use the following test code
G = nx.Graph([
(1,"a"), (1,"c"),
(2,"a"), (2,"b"),
(3,"a"), (3,"c"),
(4,"d"), (4,"e"),(4,"f"),(4,"g"),
(5,"b"), (5,"c"),
(6,"c"), (6,"d")
])
matching = HopcroftKarp(G).match()
print matching
Unfortunately this does not work, I end up in an endless loop :(. Can someone spot the error, I am out of ideas and I must admit that I have not yet fully understand the algorithm, so it is mostly an implementation of the pseudo code on wikipedia
The line
if self.pair[v] and self.dfs(v):
should be
if self.pair[v] is None and self.dfs(v):
as per the pseudo-code on the Wikipedia page. The only other problem I see is that you are using the deque as a stack and you want to use it as a queue. To remedy that, you just need to popleft rather than pop (which pops right). So the line
v = self.q.pop()
should be
v = self.q.popleft()
Hopefully everything else works. I was just checking that your Python code works in the same manner as the pseudocode on Wikipedia so hopefully that pseudocode is correct.
In python there is a package for this algorithm.
HopcroftKarp, you can directly use that package for your implementation.

Categories