I am using a simple heapq in python with custom elements on which I implemented the lt function.
class Edge:
def __init__(self, cost, u, v):
self.u = u
self.v = v
self.cost = cost
def weight(self):
w = self.cost
v = self.v
while v.parent is not None:
w += v.const
v = v.parent
return w
def __lt__(self, other):
return self.weight() < other.weight()
Then I keep a heap of these elements in another array called P:
class Vertex:
def __init__(self, node=None):
#other stuff omited #####
self.P = []
def add_incoming_nodes(self, subgraph):
for node, costs in subgraph.items():
#if costs[self.vertex] is not 0: #node is not self
#push endpoints of the edge from another vertex to this vertex
heapq.heappush(self.P, Edge(costs[self.vertex], node, self))
The problem is that when I heappop an element, I would expect it to be the smallest element in my array right ? But this assertion here fails
#select arbitrary vertex
a = all_nodes[0]
while a.P: #while P[a] is not ∅
edge = heapq.heappop(a.P)
for a_edge in a.P:
assert edge.weight() < a_edge.weight()
Related
I'm trying to create a Uniform Cost Search algorithm. but I'm having a problem in storing nodes in the priorityqueue.
It works well till node D as shown in the output provided, and I'm not sure why. Any help will be appreciated.
The error says it can't compare nodes but I'm adding them as tuples so it can use the distacne for comparison
class GraphEdge(object):
def __init__(self, destinationNode, distance):
self.node = destinationNode
self.distance = distance
class GraphNode(object):
def __init__(self, val):
self.value = val
self.edges = []
def add_child(self, node, distance):
self.edges.append(GraphEdge(node, distance))
def remove_child(self, del_node):
if del_node in self.edges:
self.edges.remove(del_node)
class Graph(object):
def __init__(self, node_list):
self.nodes = node_list
def add_edge(self, node1, node2, distance):
if node1 in self.nodes and node2 in self.nodes:
node1.add_child(node2, distance)
node2.add_child(node1, distance)
def remove_edge(self, node1, node2):
if node1 in self.nodes and node2 in self.nodes:
node1.remove_child(node2)
node2.remove_child(node1)
from queue import PriorityQueue
def build_path(root_node, goal_node):
path = [goal_node]
add_parent(root_node, goal_node, path)
return path
def add_parent(root_node, node, path):
parent = node.parent
path.append(parent)
if parent == root_node:
return
else:
add_parent(root_node, parent, path)
def ucs_search(root_node, goal_node):
visited = set()
queue = PriorityQueue()
queue.put((0, root_node))
visited_order = []
while not queue.empty():
current_node_priority, current_node = queue.get()
visited.add(current_node)
visited_order.append(current_node.value)
print("current_node:", current_node.value)
if current_node == goal_node:
print(visited_order)
return current_node, build_path(root_node, goal_node)
for edge in current_node.edges:
child = edge.node
if child not in visited:
child.parent = current_node
print("child:", child.value)
queue.put(((current_node_priority + edge.distance), child))
node_u = GraphNode('U')
node_d = GraphNode('D')
node_a = GraphNode('A')
node_c = GraphNode('C')
node_i = GraphNode('I')
node_t = GraphNode('T')
node_y = GraphNode('Y')
graph = Graph([node_u, node_d, node_a, node_c, node_i, node_t, node_y])
graph.add_edge(node_u, node_a, 4)
graph.add_edge(node_u, node_c, 6)
graph.add_edge(node_u, node_d, 3)
graph.add_edge(node_d, node_c, 4)
graph.add_edge(node_a, node_i, 7)
graph.add_edge(node_c, node_i, 4)
graph.add_edge(node_c, node_t, 5)
graph.add_edge(node_i, node_y, 4)
graph.add_edge(node_t, node_y, 5)
goal, sequence = ucs_search(node_a, node_y)
Output:
current_node: A
child: U
child: I
current_node: U
child: C
child: D
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-52-2d575db64232> in <module>
19 graph.add_edge(node_t, node_y, 5)
20
---> 21 goal, sequence = ucs_search(node_a, node_y)
<ipython-input-51-b26ec19983b6> in ucs_search(root_node, goal_node)
36 child.parent = current_node
37 print("child:", child.value)
---> 38 queue.put(((current_node_priority + edge.distance), child))
39
~\AppData\Local\Continuum\anaconda3\lib\queue.py in put(self, item, block, timeout)
147 raise Full
148 self.not_full.wait(remaining)
--> 149 self._put(item)
150 self.unfinished_tasks += 1
151 self.not_empty.notify()
~\AppData\Local\Continuum\anaconda3\lib\queue.py in _put(self, item)
231
232 def _put(self, item):
--> 233 heappush(self.queue, item)
234
235 def _get(self):
TypeError: '<' not supported between instances of 'GraphNode' and 'GraphNode'
If two tuples in the queue have the same distance, the priority queue needs to tiebreak based on the priority value of the corresponding GraphNodes. Since the __lt__ function isn't defined for GraphNodes, this will cause an error. (The __lt__ function defines how two GraphNodes can be compared using the < operator.)
To resolve, define the __lt__ function for the GraphNode class. This is the function that Python calls when comparing two GraphNodes:
class GraphNode(object):
def __init__(self, val):
self.value = val
self.edges = []
def add_child(self, node, distance):
self.edges.append(GraphEdge(node, distance))
def remove_child(self, del_node):
if del_node in self.edges:
self.edges.remove(del_node)
def __lt__(self, other):
return self.value < other.value
I have a graph object, which in turn consists of vertex objects and edge objects.
I have to copy the entire graph object into a new Graph object.
Using deepcopy is proving to be highly inefficient, don't know why.
It takes 170 secs out of total runtime of 200 secs just to copy using deepcopy.
I request you to help me please.
One execution of deepcopy takes around 1 sec. Since, there is a loop, so all executions of deepcopy account for 170 secs out of total 200 secs
Sharing the code below:
import copy
class Vertex:
def __init__(self, key):
self.key = key
self.label = None
self.neighbors = set()
self.incident_edges = set()
self.in_left = None
def get_edge(self, neighbor):
for e in self.incident_edges:
if neighbor in e.vertices:
return e
return False
def set_label(self, label):
'''Label the vertex.'''
self.label = label
def set_in_left(self, in_left):
self.in_left = in_left
def filter_neighbors(self):
new_neighbors = set()
for v in self.neighbors:
for e in self.incident_edges:
if v == e.vertices[0] or v == e.vertices[1]:
new_neighbors.add(v)
break
self.neighbors = new_neighbors
class Edge:
def __init__(self, v1, v2, weight = math.inf, heur = False):
'''Edge constructor.
Parameters
----------
v1 : str, required (endpoint1 key)
v2 : str, required (endpoint2 key)
weight : int, optional (default = inf)
'''
self.vertices = [v1, v2]
self.weight = weight
if heur == True:
self.typeOfWeight = 'h' # can be 'h' - for heuristic or 'a' - for actual weight
else:
self.typeOfWeight = None
def get_pair_vertex(self, v):
for vertex in self.vertices:
if ( vertex != v.key ):
return vertex
def __eq__(self, e):
'''Edges with equal endpoints and weights are equal.'''
return (self.vertices == e.vertices
and self.weight == e.weight)
def __hash__(self):
'''Hash the vertices (frozen set) and weight.'''
return hash((frozenset(self.vertices), self.weight))
# ------------------------------------------------------------------------------------------------------------------------
class Graph:
def __init__( self, G = {}, heur = False ):
self.vertices = {}
# If the input graph is in the form of an adjacency matrix (then we won't have vertex labels such as 'r1', 't1' in the input; rather we need to create them)
goals_lessThan_robots = False # change 999
if type(G) is not dict:
# change 999 - for handling case when no of robots != no of goals
if G.shape[1] < G.shape[0]: # that is, if the no of goals is < nof of robots, then we change the left-right orientation
goals_lessThan_robots = True
for i in range(G.shape[0]):
v1 = 'r' + str(i)
for j in range(G.shape[1]):
v2 = 'g' + str(j)
# self.add_edge(v1, v2, G[i][j]) change 999
self.add_edge(v1, v2, G[i][j], goals_lessThan_robots, heur) # change 999
else: # if the input graph is in the form of dictionary
for v1 in G:
for v2 in G[v1]:
# self.add_edge(v1, v2, G[v1][v2]) # change 999
self.add_edge(v1, v2, G[v1][v2], goals_lessThan_robots, heur) # change 999
def add_vertex(self, key):
'''
Adds a vertex to the graph.
'''
self.vertices[key] = Vertex(key) # aks - instantiates a Vertex object and adds in Graphs' vertex dictionary
def add_edge( self, v1, v2, weight = math.inf, goals_lessThan_robots = False, heur = False ):
'''
Adds an edge to the graph.
'''
if v1 not in self.vertices:
self.add_vertex(v1)
if v2 not in self.vertices:
self.add_vertex(v2)
e = Edge(v1, v2, weight, heur)
self.vertices[v1].neighbors.add(v2)
self.vertices[v2].neighbors.add(v1)
self.vertices[v1].incident_edges.add(e)
self.vertices[v2].incident_edges.add(e)
self.vertices[v1].set_in_left(True) # aks 98
self.vertices[v2].set_in_left(False) # aks 98
def edge_in_equality_subgraph(self, e):
'''
Determines whether edge is in equality subgraph
'''
e_endpoints = list(e.vertices)
if (self.vertices[e_endpoints[0]].label == None or
self.vertices[e_endpoints[1]].label == None):
return False
return e.weight == (self.vertices[e_endpoints[0]].label +
self.vertices[e_endpoints[1]].label)
def equality_subgraph(self):
'''
Creates an equality subgraph with respect to labeling
'''
eq_H = copy.deepcopy(self)
for v in eq_H.vertices:
eq_H.vertices[v].incident_edges = list(filter(
self.edge_in_equality_subgraph,
eq_H.vertices[v].incident_edges))
eq_H.vertices[v].filter_neighbors()
return eq_H
In another method, I have below calls:
G = Graph( _G, heur = True )
eq_G = G.equality_subgraph()
The first line in equality_subgraph function is using deepcopy and is the culprit.
I created a bipartite graph, with 100 vertices on left and 100 vertices on right, so 200 vertices in total.
Code is in Python.
There is a graph object, which in turn consists of vertex objects and edge objects.
I have to copy the complete graph object into a new graph object.
Using deepcopy is proving to be highly inefficient, not sure why.
It takes 170 secs out of total runtime of 200 secs just to copy using deepcopy.
One execution of deepcopy takes around 1 sec. Since, there is a loop, so all executions of deepcopy account for 170 secs out of total 200 secs
Sharing the code below:
import copy
class Vertex:
def __init__(self, key):
self.key = key
self.label = None
self.neighbors = set()
self.incident_edges = set()
self.in_left = None
def get_edge(self, neighbor):
for e in self.incident_edges:
if neighbor in e.vertices:
return e
return False
def set_label(self, label):
self.label = label
def set_in_left(self, in_left):
self.in_left = in_left
def filter_neighbors(self):
new_neighbors = set()
for v in self.neighbors:
for e in self.incident_edges:
if v == e.vertices[0] or v == e.vertices[1]:
new_neighbors.add(v)
break
self.neighbors = new_neighbors
class Edge:
def __init__(self, v1, v2, weight = math.inf, heur = False):
'''Edge constructor.
Parameters
----------
v1 : str, required (endpoint1 key)
v2 : str, required (endpoint2 key)
weight : int, optional (default = inf)
'''
self.vertices = [v1, v2]
self.weight = weight
if heur == True:
self.typeOfWeight = 'h' # can be 'h' - for heuristic or 'a' - for actual weight
else:
self.typeOfWeight = None
def get_pair_vertex(self, v):
for vertex in self.vertices:
if ( vertex != v.key ):
return vertex
def __eq__(self, e):
'''Edges with equal endpoints and weights are equal.'''
return (self.vertices == e.vertices
and self.weight == e.weight)
def __hash__(self):
'''Hash the vertices (frozen set) and weight.'''
return hash((frozenset(self.vertices), self.weight))
# ------------------------------------------------------------------------------------------------------------------------
class Graph:
def __init__( self, G = {}, heur = False ):
self.vertices = {}
# If the input graph is in the form of an adjacency matrix (then we won't have vertex labels such as 'r1', 't1' in the input; rather we need to create them)
goals_lessThan_robots = False # change 999
if type(G) is not dict:
# change 999 - for handling case when no of robots != no of goals
if G.shape[1] < G.shape[0]: # that is, if the no of goals is < nof of robots, then we change the left-right orientation
goals_lessThan_robots = True
for i in range(G.shape[0]):
v1 = 'r' + str(i)
for j in range(G.shape[1]):
v2 = 'g' + str(j)
# self.add_edge(v1, v2, G[i][j]) change 999
self.add_edge(v1, v2, G[i][j], goals_lessThan_robots, heur) # change 999
else: # if the input graph is in the form of dictionary
for v1 in G:
for v2 in G[v1]:
# self.add_edge(v1, v2, G[v1][v2]) # change 999
self.add_edge(v1, v2, G[v1][v2], goals_lessThan_robots, heur)
def add_vertex(self, key):
'''
Adds a vertex to the graph.
'''
self.vertices[key] = Vertex(key) # aks - instantiates a Vertex object and adds in Graphs' vertex dictionary
def add_edge( self, v1, v2, weight = math.inf, goals_lessThan_robots = False, heur = False ):
'''
Adds an edge to the graph.
'''
if v1 not in self.vertices:
self.add_vertex(v1)
if v2 not in self.vertices:
self.add_vertex(v2)
e = Edge(v1, v2, weight, heur)
self.vertices[v1].neighbors.add(v2)
self.vertices[v2].neighbors.add(v1)
self.vertices[v1].incident_edges.add(e)
self.vertices[v2].incident_edges.add(e)
self.vertices[v1].set_in_left(True) # aks 98
self.vertices[v2].set_in_left(False) # aks 98
def edge_in_equality_subgraph(self, e):
'''
Determines whether edge is in equality subgraph
'''
e_endpoints = list(e.vertices)
if (self.vertices[e_endpoints[0]].label == None or
self.vertices[e_endpoints[1]].label == None):
return False
return e.weight == (self.vertices[e_endpoints[0]].label +
self.vertices[e_endpoints[1]].label)
def equality_subgraph(self):
'''
Creates an equality subgraph with respect to labeling
'''
eq_H = copy.deepcopy(self)
for v in eq_H.vertices:
eq_H.vertices[v].incident_edges = list(filter(
self.edge_in_equality_subgraph,
eq_H.vertices[v].incident_edges))
eq_H.vertices[v].filter_neighbors()
return eq_H
In another method, I have below calls:
G = Graph( _G, heur = True )
eq_G = G.equality_subgraph()
The first line in equality_subgraph function is using deepcopy and is the culprit.
I created a bipartite graph, with 100 vertices on left and 100 vertices on right, so 200 vertices in total.
Request for help.
I've been working on a school assignment, where I need to implement Dijkstra's algorithm. That wouldn't be too hard by itself but unfortunately, the automatic checking script disagrees with all of my implementations (I actually made like 8 different versions). All the initial data checking works correctly, only when the script generates random data, it differs. My path and script's path has the same distance, but different vertexes on the path. For example:
Teachers path: City2, City15, City16, City6,
Students path: City2, City15, City18, City0, City6,
I even contacted the teacher who just responded with "You have to use priority queue :-)" despite me using one (in fact, several implementations of one, from my own to heapq). Am I doing something wrong or is it the teacher script that's incorrect? I hope the code is self-commenting enough to be understandable. Thank you for any advice you can give me.
The algorithm is called on source vertex and computes shortest distance and path to every other connected node. If the vertex has same minDistance (ie. priority) as some that's already there, it should go in front of it, not after it.
class Node:
"""Basic node of the priority queue"""
def __init__(self, data, priority):
self.data = data
self.nextNode = None
self.priority = priority
self.id = data.id
class PriorityQueue:
"""Basic priority queue with add, remove and update methods"""
def __init__(self):
self.head = None
self.count = 0
def add(self, data, priority):
"""Adds data with priority in the proper place"""
node = Node(data, priority)
if not self.head:
self.head = node
elif node.priority <= self.head.priority:
node.nextNode = self.head
self.head = node
else:
checker = self.head
while True:
if not checker.nextNode or node.priority >= checker.nextNode.priority:
break
checker = checker.nextNode
node.nextNode = checker.nextNode
checker.nextNode = node
return 0
def remove(self, data):
"""Removes specified node and reconnects the remaining nodes, does nothing if node not found"""
checker = self.head
if not self.head:
return 0
if checker.id == data.id:
self.head = checker.nextNode
while True:
checker = checker.nextNode
if not checker or not checker.nextNode:
return 0
if checker.nextNode.id == data.id:
checker.nextNode = checker.nextNode.nextNode
break
return 0
def update(self, data):
"""Updates priority of existing node via removing and re-adding it"""
self.remove(data)
self.add(data, data.minDistance)
return 0
def getMin(self):
"""Returns the minimum priority data"""
min = self.head
return min.data
class Edge:
"""Edge of the graph, contains source, target and weight of line"""
def __init__(self, source, target, weight):
self.source = source
self.target = target
self.weight = weight
class Vertex:
"""Vertex of the graph, everything except id and name is filled later"""
def __init__(self, id, name):
self.id = id
self.name = name
self.minDistance = float('inf')
self.previousVertex = None
self.edges = []
self.visited = False
class Dijkstra:
"""Dijkstra's algorithm implementation"""
def __init__(self):
self.vertexes = []
self.nodes = {}
self.unvisited = PriorityQueue()
def createGraph(self, vertexes, edgesToVertexes):
"""Connects edges to appropriate vertexes, adds vertexes to node dictionary"""
self.vertexes = vertexes
for vertex in self.vertexes:
for edge in edgesToVertexes:
if vertex.id == edge.source:
vertex.edges.append(edge)
edgesToVertexes.remove(edge)
self.nodes[vertex.id] = vertex
return 0
def getVertexes(self):
"""Returns vertexes in graph, should be called after creating it just to check"""
return self.vertexes
def computePath(self, sourceId):
"""Fills in minDistance and previousVertex of all nodes from source"""
mainNode = self.nodes[sourceId]
mainNode.minDistance = 0
self.unvisited.add(mainNode, 0)
while self.unvisited.head:
mainNode = self.unvisited.getMin()
mainNode.visited=True
for edge in mainNode.edges:
tempDistance = mainNode.minDistance + edge.weight
targetNode = self.nodes[edge.target]
self.unvisited.remove(mainNode)
if tempDistance < targetNode.minDistance:
targetNode.minDistance = tempDistance
targetNode.previousVertex = mainNode
self.unvisited.update(targetNode)
return 0
def getShortestPathTo(self, targetId):
"""Returns list of shortest parth to targetId from source. Call only after doing ComputePath"""
path = []
mainNode = self.nodes[targetId]
while True:
path.append(mainNode)
mainNode = mainNode.previousVertex
if not mainNode:
break
return list(reversed(path))
def resetDijkstra(self):
"""Resets ComputePath but leaves graph untouched"""
for vertex in self.vertexes:
vertex.minDistance = float('inf')
vertex.previousVertex = None
return 0
def createGraph(self, vertexes, edgesToVertexes):
"""Connects edges to appropriate vertexes, adds vertexes to node dictionary"""
self.vertexes = vertexes
for vertex in self.vertexes:
for edge in edgesToVertexes:
if vertex.id == edge.source:
vertex.edges.append(edge)
edgesToVertexes.remove(edge)
self.nodes[vertex.id] = vertex
return 0
I belive this was wrong => edgesToVertexes.remove(edge)
I had similar home work and used some of your code and this one line was incorrect I think. It removed one path from vortex in every loop.
I am currently developing an AI system in python to solve the bloxorz game using the A* search algorithm.
Naturally, the algorithm stores the nodes in a priority queue,but when i try to get() an element from the queue, it returns an int instead of the object. As I am very new to python, I would appreciate it if someone can clarify.
My A* algorithm:
class Astar:
def __init__(self, start):
self.path = []
self.visitedQueue = []
"""hold visited in a queue to avoid duplicates"""
self.priorityQueue = PriorityQueue()
self.start = start
def Solve(self):
"""the a* algorithm"""
StartNode = Node_Map(self.start, 0, self.start)
count = 0
self.priorityQueue.put(0, count, StartNode)
while not self.path and self.priorityQueue.qsize():
closestChild = self.priorityQueue.get()[2]
closestChild.createChildren()
self.visitedQueue.append(closestChild.matrix)
for child in closestChild.children:
if child.matrix not in self.visitedQueue:
count += 1
if child.getH == 0:
self.path = child.path
break
self.priorityQueue.put(child.getH+count, count, child)
""" put in priority queue according to f(n)=h(n)+g(n)"""
if not self.path:
print("goal not possible")
return self.path
My Node class and Node_Map class:
class Node(object):
def __init__(self, matrix, parent, start=0):
self.children = []
self.matrix = {}
self.parent = parent
self.xPos = 0
self.yPos = 0
self.goalX = 0
self.goalY = 0
if parent:
self.path = parent.path[:]
self.start = parent.start
self.path.append = [matrix]
else:
self.path = [matrix]
self.start = start
def getDist(self):
""" abstract function to get our estimated distance to the goal"""
pass
def createChildren(self):
"""absract to create children from successor actions"""
pass
class Node_Map(Node):
def __init__(self, matrix, parent, start=0):
super(Node_Map, self).__init__(matrix, parent, start)
self.h = self.getH()
priorityQueue.put(child.getH+count, count, child)
The above line calls put with the arguments: item=child.getH+count, block=count, and timeout=child. As a result, only child.getH+count is be considered as the 'item' that get will retrieve. Try putting all three objects into a tuple:
priorityQueue.put((child.getH+count, count, child))
This way, item will be the tuple (child.getH+count, count, child), and the other two arguments, block, and timeout, will stay as their default values (True and None, respectively).