Python: Heapify a list of tuples (Dijkstra's Algo) - python

Here is my code for Dijkstra's Algorithm.
I have declared a "Vertex" class and a "Graph" class.
I am using heapq module and heapifying the list "unvisitedQueue" of tuples. But even then an error shows up saying " TypeError: '<' not supported between instances of 'Vertex' and 'Vertex' " even when "v.getDistance()" returns either 0 or float('inf').
import heapq
class Vertex:
def __init__(self, node):
self.id = node
self.adjacent = {}
self.previous = None
self.distance = float('inf')
def addNeighbor(self, neighbor, weight = 0):
self.adjacent[neighbor] = weight
def getConnections(self):
return self.adjacent.keys()
def getVertex_ID(self):
return self.id
def getWeight(self, neighbor):
return self.adjacent[neighbor]
def setDistance(self, dist):
self.distance = dist
def getDistance(self):
return self.distance
def setPrevious(self, prev):
self.previous = prev
def __str__(self):
return str(self.id) + "adjacent : " + str([x.id for x in self.adjacent])
class Graph:
def __init__(self):
self.vertDictionary = {}
self.numVertices = 0
def __iter__(self):
return iter(self.vertDictionary.values())
def addVertex(self, node):
self.numVertices += 1
newVertex = Vertex(node)
self.vertDictionary[node] = newVertex
return newVertex
def getVertex(self, node):
if node in self.vertDictionary:
return self.vertDictionary[node]
else:
return None
def addEdge(self, frm, to, cost = 0):
if frm not in self.vertDictionary:
self.addVertex(frm)
if to not in self.vertDictionary:
self.addVertex(to)
self.vertDictionary[frm].addNeighbor(self.vertDictionary[to], cost)
self.vertDictionary[to].addNeighbor(self.vertDictionary[frm], cost)
def getVertices(self):
return self.vertDictionary.keys()
def setPrevious(self, current):
self.previous = current
def getPrevious(self):
return self.previous
def getEdges(self):
edges = []
for v in G:
for w in v.getConnections():
v_id = v.getVertex_ID()
w_id = w.getVertex_ID()
edges.append((v_id, w_id, v.getWeight(w)))
return edges
def Dijkstra(G, s):
source = G.getVertex(s)
source.setDistance(0)
visited = {}
unvisitedQueue = [(v.getDistance(), v) for v in G]
heapq.heapify(unvisitedQueue)
while len(unvisitedQueue):
uv = heapq.heappop(unvisitedQueue)
currVert = uv[1]
visited[currVert] = True
for nbr in currVert.getConnections():
if currVert.getDistance() + currVert.getWeight(nbr) < nbr.getDistance():
nbr.setDistance(currVert.getDistance() + currVert.getWeight(nbr))
print("Updated: Current = %s Neighbour = %s New Distance = %s" %(currVert.getVertex_ID(), nbr.getVertex_ID(), nbr.getDistance()))
else:
print("Not Updated: Current = %s Neighbour = %s Distance = %s" %(currVert.getVertex_ID(), nbr.getVertex_ID(), nbr.getDistance()))
while len(unvisitedQueue):
heapq.heappop(unvisitedQueue)
unvisitedQueue = [(v.getDistance(), v) for v in G if v not in visited]
heapq.heapify(unvisitedQueue)
for v in G:
print(source.getVertex_ID(), "to", v.getVertex_ID(), "-->", v.getDistance())
ERROR -->
Traceback (most recent call last):
File "d:\Python\Final 450\Graph\Dijkstra's_Algo.py", line 124, in <module>
print(Dijkstra(G, "a"))
File "d:\Python\Final 450\Graph\Dijkstra's_Algo.py", line 86, in Dijkstra
heapq.heapify(unvisitedQueue)
TypeError: '<' not supported between instances of 'Vertex' and 'Vertex'

The error is happening because tuples are compared lexicographically. If two distances are the same, the comparison moves on to the Vertex objects themselves.
Two solutions readily come to mind. The first is simply to add a unique index to the tuple before the Vertex but after the distance. This is easy, and would work even if you didn't have access to the Vertex class:
unvisitedQueue = [(v.getDistance(), i, v) for i, v in enumerate(G) if v not in visited]
The second option is to modify Vertex to have a __lt__ magic method:
def __lt__(self, other):
return self.getDistance() < other.getDistance()
This is nice because you can heapify more directly now:
unvisitedQueue = [v for v in G if v not in visited]

Related

Calculates the Kevin Bacon number of an actor in graph

I have made Queue Class and Graph classes
1st: Program a function
kevin_bacon_number(moviegraph, actor)
that calculates the Kevin Bacon number of an actor in your graph.
Testing: The code
g = Graph('movies.txt', False, '/')
kevin_bacon_number(g, 'De Niro, Robert')
should result in 1 (they were both in the same movie) and 2nd.
Program a function
actors_with_number(moviegraph, number)
That calculates the list of actors that have number as Kevin Bacon number.
Testing: The code
g = Graph('movies.txt', False, '/')
actors_with_number(g, 0)
should result in Bacon, Kevin. and 'De Niro, Robert' in actors_with_number(g, 1) should be True.
class Queue:
#-------------------------------------------------------------------
# Construct Queue object self as an empty Queue object.
def __init__(self):
self._first = None # Reference to first _Node
self._last = None # Reference to last _Node
self._length = 0 # Number of items
#-------------------------------------------------------------------
# Return True if self is empty, and False otherwise.
def isEmpty(self):
return self._first is None
#-------------------------------------------------------------------
# Add item to the end of self.
def enqueue(self, item):
oldLast = self._last
self._last = _Node(item, None)
if self.isEmpty():
self._first = self._last
else:
oldLast.next = self._last
self._length += 1
#-------------------------------------------------------------------
# Remove the first item of self and return it.
def dequeue(self):
item = self._first.item
self._first = self._first.next
if self.isEmpty():
self._last = None
self._length -= 1
return item
#-------------------------------------------------------------------
# Return the number of items in self.
def __len__(self):
return self._length
#-------------------------------------------------------------------
# Return a string representation of self.
def __str__(self):
s = ''
cur = self._first
while cur is not None:
s += str(cur.item) + ' '
cur = cur.next
return s
#----------------------------------------------------------------------
A _Node object references an item and a next _Node object.
A Queue object is composed of _Node objects.
class _Node:
def __init__(self, item, next):
self.item = item # Reference to an item
self.next = next # Reference to the next _Node object
class Graph:
# Construct a new Graph object. If a filename is specified,
# populate the Graph object by reading data from the specified
# file with the specified delimiter.
# For directed graphs the argument directed should get value True
def __init__(self, filename=None, directed=False, delimiter=None):
self._directed = directed
self._e = 0
self._adj = dict()
if filename is not None:
f = open(filename, 'r')
lines = f.read().split('\n')
for line in lines:
names = line.split(delimiter)
for i in range(1, len(names)):
self.addEdge(names[0], names[i])
line = ''
# Add an edge to self between vertex v and vertex w.
def addEdge(self, v, w):
if not self.hasVertex(v): self._adj[v] = set()
if not self.hasVertex(w): self._adj[w] = set()
if not self.hasEdge(v, w):
self._e += 1
self._adj[v].add(w)
if not self._directed: self._adj[w].add(v)
# Return an iterable collection containing all neighbors of
# vertex v in self.
def adjacentTo(self, v):
return iter(self._adj[v])
# Return an iterable collection of all vertices in self.
def vertices(self):
return iter(self._adj)
# Return True if vertex v is in self, and False otherwise.
def hasVertex(self, v):
return v in self._adj
# Return True if v-w is an edge in self, and False otherwise.
def hasEdge(self, v, w):
return w in self._adj[v]
# Return the number of vertices in self.
def countV(self):
return len(self._adj)
# Return the number of edges in self.
def countE(self):
return self._e
# Return the degree of vertex v of self.
def degree(self, v):
return len(self._adj[v])
# Return a string representation of self.
def __str__(self):
s = ''
for v in self.vertices():
s += v + ' '
for w in self.adjacentTo(v):
s += w + ' '
s += '\n'
return s

Dijkstra with a Priority Queue (Python)

I have been trying to use Dijkstra's algorithm with an implementation of a priority queue and a distance table, in Python.
This is the priority queue implementation:
from heapq import heapify, heappush, heappop
class priority_dict(dict):
def __init__(self, *args, **kwargs):
super(priority_dict, self).__init__(*args, **kwargs)
self._rebuild_heap()
def _rebuild_heap(self):
self._heap = [(v, k) for k, v in self.items()]
heapify(self._heap)
def smallest(self):
heap = self._heap
v, k = heap[0]
while k not in self or self[k] != v:
heappop(heap)
v, k = heap[0]
return k
def pop_smallest(self):
heap = self._heap
v, k = heappop(heap)
while k not in self or self[k] != v:
v, k = heappop(heap)
del self[k]
return k
def __setitem__(self, key, val):
super(priority_dict, self).__setitem__(key, val)
if len(self._heap) < 2 * len(self):
heappush(self._heap, (val, key))
else:
self._rebuild_heap()
def setdefault(self, key, val):
if key not in self:
self[key] = val
return val
return self[key]
def update(self, *args, **kwargs):
super(priority_dict, self).update(*args, **kwargs)
self._rebuild_heap()
def sorted_iter(self):
while self:
yield self.pop_smallest()
And this is the Dijkstra implementation:
import priority_dict
from graph import *
def build_distance_table(graph, source):
distance_table = {}
for i in range(graph.numVertices):
distance_table[i] = (None, None)
distance_table[source] = (0, source)
priority_queue = priority_dict.priority_dict()
priority_queue[source] = 0
while len(priority_queue.keys()) > 0:
current_vertex = priority_queue.pop_smallest()
current_distance = distance_table[current_vertex][0]
for neighbor in graph.get_adjacent_vertices(current_vertex):
distance = current_distance + g.get_edge_weight(current_vertex, neighbor)
neighbor_distance = distance_table[neighbor][0]
if neighbor_distance is None or neighbor_distance > distance:
distance_table[neighbor] = (distance, current_vertex)
priority_queue = distance
return distance_table
def shortest_path(graph, source, destination):
distance_table = build_distance_table(graph, source)
path = [destination]
previous_vertex = distance_table[destination][1]
while previous_vertex and previous_vertex is not source:
path = [previous_vertex] + path
previous_vertex = distance_table[previous_vertex][1]
if previous_vertex is None:
print("There is no path from %d to %d" % (source, destination))
else:
path: [source] + path
print("Shortest path is: ", path)
This is the result:
Traceback (most recent call last):
File "dijkstra.py", line 76, in <module>
shortest_path(g, 0, 6)
File "dijkstra.py", line 46, in shortest_path
distance_table = build_distance_table(graph, source)
File "dijkstra.py", line 23, in build_distance_table
while len(priority_queue.keys()) > 0:
AttributeError: 'numpy.float64' object has no attribute 'keys'
I am using Python 3.7. I have searched online and though it had to do with the Python version. Can't figure why it doesn't see the attribute. Could you please tell me what am I missing?
priority_queue = distance
Should have been:
priority_queue[neighbor] = distance
Solved it, thank you.

Bidirectional A* not finding the shortest path

I'm implementing bidirectional A* algorithm in Python 2.7.12 and testing it on the map of Romania from from Russell and Norvig, Chapter 3. The edges have weights and the aim is to find the shortest path between two nodes.
Here is the visualization of the testing graph:
The example where my Bidirectional A* is failing is that where the starting point is 'a' and the goal is 'u'. This is the path that my implementation has found:
The length of ['a', 's', 'f', 'b', 'u'] is 535.
This is the actual shortest path from 'a' to 'u':
The length of ['a', 's', 'r', 'p', 'b', 'u'] is 503.
As we can see, my implementation failed to find the shortest path. I think that the problem may be in my stopping conditions, but I don't know.
This is the python script with my implementation of A* (I used Euclidean distance as a heuristic) and few other help classes and functions:
from __future__ import division
import math
from networkx import *
import random
import pickle
import sys
import heapq
import matplotlib.pyplot as plt
class PriorityQueue():
"""Implementation of a priority queue"""
def __init__(self):
self.queue = []
self.node_finder = dict()
self.current = 0
self.REMOVED_SYMBOL = '<removed>'
def next(self):
if self.current >=len(self.queue):
self.current
raise StopIteration
out = self.queue[self.current]
self.current += 1
return out
def pop(self):
while self.queue:
node = heapq.heappop(self.queue)
nodeId = node[1]
if nodeId is not self.REMOVED_SYMBOL:
try:
del self.node_finder[nodeId]
except KeyError:
dummy=1
return node
def remove(self, nodeId):
node = self.node_finder[nodeId]
node[1] = self.REMOVED_SYMBOL
def __iter__(self):
return self
def __str__(self):
return 'PQ:[%s]'%(', '.join([str(i) for i in self.queue]))
def append(self, node):
nodeId = node[1]
nodePriority = node[0]
node = [nodePriority, nodeId]
self.node_finder[nodeId] = node
heapq.heappush(self.queue, node)
def update(self, node):
nodeId = node[1]
nodePriority = node[0]
node = [nodePriority, nodeId]
self.remove(nodeId)
self.node_finder[nodeId] = node
heapq.heappush(self.queue, node)
def getPriority(self, nodeId):
return self.node_finder[nodeId][0]
def __contains__(self, key):
self.current = 0
return key in [n for v,n in self.queue]
def __eq__(self, other):
return self == other
def size(self):
return len(self.queue)
def clear(self):
self.queue = []
def top(self):
return self.queue[0]
__next__ = next
def bidirectional_a_star(graph, start, goal):
if start == goal:
return []
pq_s = PriorityQueue()
pq_t = PriorityQueue()
closed_s = dict()
closed_t = dict()
g_s = dict()
g_t = dict()
g_s[start] = 0
g_t[goal] = 0
cameFrom1 = dict()
cameFrom2 = dict()
def euclidean_distance(graph, v, goal):
xv, yv = graph.node[v]['pos']
xg, yg = graph.node[goal]['pos']
return ((xv-xg)**2 + (yv-yg)**2)**0.5
def h1(v): # heuristic for forward search (from start to goal)
return euclidean_distance(graph, v, goal)
def h2(v): # heuristic for backward search (from goal to start)
return euclidean_distance(graph, v, start)
cameFrom1[start] = False
cameFrom2[goal] = False
pq_s.append((0+h1(start), start))
pq_t.append((0+h2(goal), goal))
done = False
i = 0
mu = 10**301 # 10**301 plays the role of infinity
connection = None
while pq_s.size() > 0 and pq_t.size() > 0 and done == False:
i = i + 1
if i % 2 == 1: # alternate between forward and backward A*
fu, u = pq_s.pop()
closed_s[u] = True
for v in graph[u]:
weight = graph[u][v]['weight']
if v in g_s:
if g_s[u] + weight < g_s[v]:
g_s[v] = g_s[u] + weight
cameFrom1[v] = u
if v in closed_s:
del closed_s[v]
if v in pq_s:
pq_s.update((g_s[v]+h1(v), v))
else:
pq_s.append((g_s[v]+h1(v), v))
else:
g_s[v] = g_s[u] + weight
cameFrom1[v] = u
pq_s.append((g_s[v]+h1(v), v))
if v in closed_t:
if g_s[u] + weight + g_t[v] < mu:
mu = g_s[u] + weight + g_t[v]
connection = v
done = True
else:
fu, u = pq_t.pop()
closed_t[u] = True
for v in graph[u]:
weight = graph[u][v]['weight']
if v in g_t:
if g_t[u] + weight < g_t[v]:
g_t[v] = g_t[u] + weight
cameFrom2[v] = u
if v in closed_t:
del closed_t[v]
if v in pq_t:
pq_t.update((g_t[v]+h2(v), v))
else:
pq_t.append((g_t[v]+h2(v), v))
else:
g_t[v] = g_t[u] + weight
cameFrom2[v] = u
pq_t.append((g_t[v]+h2(v), v))
if v in closed_s:
if g_t[u] + weight + g_s[v] < mu:
mu = g_t[u] + weight + g_s[v]
connection = v
done = True
if u in closed_s and u in closed_t:
if g_s[u] + g_t[u] < mu:
mu = g_s[u] + g_t[u]
connection = u
stopping_distance = min(min([f for (f,x) in pq_s]), min([f for (f,x) in pq_t]))
if mu <= stopping_distance:
done = True
#connection = u
continue
if connection is None:
# start and goal are not connected
return None
#print cameFrom1
#print cameFrom2
path = []
current = connection
#print current
while current != False:
#print predecessor
path = [current] + path
current = cameFrom1[current]
current = connection
successor = cameFrom2[current]
while successor != False:
path = path + [successor]
current = successor
successor = cameFrom2[current]
return path
# This function visualizes paths
def draw_graph(graph, node_positions={}, start=None, goal=None, path=[]):
explored = list(graph.get_explored_nodes())
labels ={}
for node in graph:
labels[node]=node
if not node_positions:
node_positions = networkx.spring_layout(graph)
edge_labels = networkx.get_edge_attributes(graph,'weight')
networkx.draw_networkx_nodes(graph, node_positions)
networkx.draw_networkx_edges(graph, node_positions, style='dashed')
networkx.draw_networkx_edge_labels(graph, node_positions, edge_labels=edge_labels)
networkx.draw_networkx_labels(graph,node_positions, labels)
networkx.draw_networkx_nodes(graph, node_positions, nodelist=explored, node_color='g')
if path:
edges = [(path[i], path[i+1]) for i in range(0, len(path)-1)]
networkx.draw_networkx_edges(graph, node_positions, edgelist=edges, edge_color='b')
if start:
networkx.draw_networkx_nodes(graph, node_positions, nodelist=[start], node_color='b')
if goal:
networkx.draw_networkx_nodes(graph, node_positions, nodelist=[goal], node_color='y')
plt.plot()
plt.show()
# this function calculates the length of the path
def calculate_length(graph, path):
pairs = zip(path, path[1:])
return sum([graph.get_edge_data(a, b)['weight'] for a, b in pairs])
#Romania map data from Russell and Norvig, Chapter 3.
romania = pickle.load(open('romania_graph.pickle', 'rb'))
node_positions = {n: romania.node[n]['pos'] for n in romania.node.keys()}
start = 'a'
goal = 'u'
path = bidirectional_a_star(romania, start, goal)
print "This is the path found by bidirectional A* :", path
print "Its length :", calculate_length(romania, path)
# visualize my path
draw_graph(romania, node_positions=node_positions, start=start, goal=goal, path=path)
# compare to the true shortest path between start and goal
true_path = networkx.shortest_path(romania, start, goal, weight='weight')
print "This is the actual shortest path: ", true_path
print "Its lenght: ", calculate_length(romania, true_path)
#visualize true_path
draw_graph(romania, node_positions=node_positions, start=start, goal=goal, path=true_path)
Pickle data for Romania can be downloaded from here.
I corrected some errors in PriorityQueue and bidirectional_a_star. It's working fine now.
The corrected code for the class and the function is as follows:
class PriorityQueue():
"""Implementation of a priority queue"""
def __init__(self):
self.queue = []
self.node_finder = dict()
self.current = 0
self.REMOVED_SYMBOL = '<removed>'
def next(self):
if self.current >=len(self.queue):
self.current
raise StopIteration
out = self.queue[self.current]
while out == self.REMOVED_SYMBOL:
self.current += 1
out = self.queue[self.current]
self.current += 1
return out
def pop(self):
# TODO: finish this
while self.queue:
node = heapq.heappop(self.queue)
nodeId = node[1]
if nodeId is not self.REMOVED_SYMBOL:
try:
del self.node_finder[nodeId]
except KeyError:
dummy=1
return node
#raise KeyError('pop from an empty priority queue')
def remove(self, nodeId):
node = self.node_finder[nodeId]
node[1] = self.REMOVED_SYMBOL
def __iter__(self):
return self
def __str__(self):
return 'PQ:[%s]'%(', '.join([str(i) for i in self.queue]))
def append(self, node):
# node = (priority, nodeId)
nodeId = node[1]
nodePriority = node[0]
node = [nodePriority, nodeId]
self.node_finder[nodeId] = node
heapq.heappush(self.queue, node)
def update(self, node):
nodeId = node[1]
nodePriority = node[0]
node = [nodePriority, nodeId]
self.remove(nodeId)
self.node_finder[nodeId] = node
heapq.heappush(self.queue, node)
def getPriority(self, nodeId):
return self.node_finder[nodeId][0]
def __contains__(self, key):
self.current = 0
return key in [n for v,n in self.queue]
def __eq__(self, other):
return self == other
def size(self):
return len([1 for priority, node in self.queue if node!=self.REMOVED_SYMBOL])
def clear(self):
self.queue = []
def top(self):
return self.queue[0]
__next__ = next
def bidirectional_a_star(graph, start, goal):
if start == goal:
return []
pq_s = PriorityQueue()
pq_t = PriorityQueue()
closed_s = dict()
closed_t = dict()
g_s = dict()
g_t = dict()
g_s[start] = 0
g_t[goal] = 0
cameFrom1 = dict()
cameFrom2 = dict()
def euclidean_distance(graph, v, goal):
xv, yv = graph.node[v]['pos']
xg, yg = graph.node[goal]['pos']
return ((xv-xg)**2 + (yv-yg)**2)**0.5
def h1(v): # heuristic for forward search (from start to goal)
return euclidean_distance(graph, v, goal)
def h2(v): # heuristic for backward search (from goal to start)
return euclidean_distance(graph, v, start)
cameFrom1[start] = False
cameFrom2[goal] = False
pq_s.append((0+h1(start), start))
pq_t.append((0+h2(goal), goal))
done = False
i = 0
mu = 10**301 # 10**301 plays the role of infinity
connection = None
while pq_s.size() > 0 and pq_t.size() > 0 and done == False:
i = i + 1
if i % 2 == 1: # alternate between forward and backward A*
fu, u = pq_s.pop()
closed_s[u] = True
for v in graph[u]:
weight = graph[u][v]['weight']
if v in g_s:
if g_s[u] + weight < g_s[v]:
g_s[v] = g_s[u] + weight
cameFrom1[v] = u
if v in closed_s:
del closed_s[v]
if v in pq_s:
pq_s.update((g_s[v]+h1(v), v))
else:
pq_s.append((g_s[v]+h1(v), v))
else:
g_s[v] = g_s[u] + weight
cameFrom1[v] = u
pq_s.append((g_s[v]+h1(v), v))
else:
fu, u = pq_t.pop()
closed_t[u] = True
for v in graph[u]:
weight = graph[u][v]['weight']
if v in g_t:
if g_t[u] + weight < g_t[v]:
g_t[v] = g_t[u] + weight
cameFrom2[v] = u
if v in closed_t:
del closed_t[v]
if v in pq_t:
pq_t.update((g_t[v]+h2(v), v))
else:
pq_t.append((g_t[v]+h2(v), v))
else:
g_t[v] = g_t[u] + weight
cameFrom2[v] = u
pq_t.append((g_t[v]+h2(v), v))
if u in closed_s and u in closed_t:
if g_s[u] + g_t[u] < mu:
mu = g_s[u] + g_t[u]
connection = u
try:
stopping_distance = max(min([f for (f,x) in pq_s]), min([f for (f,x) in pq_t]))
except ValueError:
continue
if mu <= stopping_distance:
done = True
connection = u
continue
if connection is None:
# start and goal are not connected
return None
#print cameFrom1
#print cameFrom2
path = []
current = connection
#print current
while current != False:
#print predecessor
path = [current] + path
current = cameFrom1[current]
current = connection
successor = cameFrom2[current]
while successor != False:
path = path + [successor]
current = successor
successor = cameFrom2[current]
return path

unorderable types: Vertex() < Vertex()

I'm working on a dijkstra and I get this error:
TypeError: unorderable types: Vertex() < Vertex()
The whole error log is:
Traceback (most recent call last):
File "C:/Users/Dimitar/PycharmProjects/Dijkstra/Dijkstra.py", line 165, in <module>
dijkstra(g, g.get_vertex('a'))
File "C:/Users/Dimitar/PycharmProjects/Dijkstra/Dijkstra.py", line 101, in dijkstra
heapq.heapify(unvisited_queue)
TypeError: unorderable types: Vertex() < Vertex()
Here is my code :
import sys
class Vertex:
def __init__(self, node):
self.id = node
self.adjacent = {}
# Set distance to infinity for all nodes
self.distance = sys.maxsize
# Mark all nodes unvisited
self.visited = False
# Predecessor
self.previous = None
def add_neighbor(self, neighbor, weight=0):
self.adjacent[neighbor] = weight
def get_connections(self):
return self.adjacent.keys()
def get_id(self):
return self.id
def get_weight(self, neighbor):
return self.adjacent[neighbor]
def set_distance(self, dist):
self.distance = dist
def get_distance(self):
return self.distance
def set_previous(self, prev):
self.previous = prev
def set_visited(self):
self.visited = True
def __str__(self):
return str(self.id) + ' adjacent: ' + str([x.id for x in self.adjacent])
class Graph:
def __init__(self):
self.vert_dict = {}
self.num_vertices = 0
def __iter__(self):
return iter(self.vert_dict.values())
def add_vertex(self, node):
self.num_vertices = self.num_vertices + 1
new_vertex = Vertex(node)
self.vert_dict[node] = new_vertex
return new_vertex
def get_vertex(self, n):
if n in self.vert_dict:
return self.vert_dict[n]
else:
return None
def add_edge(self, frm, to, cost=0):
if frm not in self.vert_dict:
self.add_vertex(frm)
if to not in self.vert_dict:
self.add_vertex(to)
self.vert_dict[frm].add_neighbor(self.vert_dict[to], cost)
self.vert_dict[to].add_neighbor(self.vert_dict[frm], cost)
def get_vertices(self):
return self.vert_dict.keys()
def set_previous(self, current):
self.previous = current
def get_previous(self, current):
return self.previous
def shortest(v, path):
''' make shortest path from v.previous'''
if v.previous:
path.append(v.previous.get_id())
shortest(v.previous, path)
return
import heapq
# noinspection PyArgumentList
def dijkstra(aGraph, start):
print('''Dijkstra's shortest path''')
# Set the distance for the start node to zero
start.set_distance(0)
# Put tuple pair into the priority queue
unvisited_queue = [(v.get_distance(), v) for v in aGraph]
heapq.heapify(unvisited_queue)
while len(unvisited_queue):
# Pops a vertex with the smallest distance
uv = heapq.heappop(unvisited_queue)
current = uv[1]
current.set_visited()
for next in current.adjacent:
# if visited, skip
if next.visited:
continue
new_dist = current.get_distance() + current.get_weight(next)
if new_dist < next.get_distance():
next.set_distance(new_dist)
next.set_previous(current)
print
('updated : current = %s next = %s new_dist = %s' \
% (current.get_id(), next.get_id(), next.get_distance()))
else:
print
('not updated : current = %s next = %s new_dist = %s' \
% (current.get_id(), next.get_id(), next.get_distance()))
# Rebuild heap
# 1. Pop every item
while len(unvisited_queue):
heapq.heappop(unvisited_queue)
# 2. Put all vertices not visited into the queue
unvisited_queue = [(v.get_distance(), v) for v in aGraph if not v.visited]
heapq.heapify(unvisited_queue)
if __name__ == '__main__':
g = Graph()
g.add_vertex('a')
g.add_vertex('b')
g.add_vertex('c')
g.add_vertex('d')
g.add_vertex('e')
g.add_vertex('f')
g.add_edge('a', 'b', 7)
g.add_edge('a', 'c', 9)
g.add_edge('a', 'f', 14)
g.add_edge('b', 'c', 10)
g.add_edge('b', 'd', 15)
g.add_edge('c', 'd', 11)
g.add_edge('c', 'f', 2)
g.add_edge('d', 'e', 6)
g.add_edge('e', 'f', 9)
print
('Graph data:')
for v in g:
for w in v.get_connections():
vid = v.get_id()
wid = w.get_id()
print
('( %s , %s, %3d)' % ( vid, wid, v.get_weight(w)))
dijkstra(g, g.get_vertex('a'))
target = g.get_vertex('e')
path = [target.get_id()]
shortest(target, path)
print('The shortest path : %s' % (path[::-1]))
Can some one explain me why I get this kind of error. I'm a self learner and some help will be really appreciated.
The line of code that causes the error is :
heapq.heapify(unvisited_queue)
Thanks in advance to everyone who comment on the topic.
Best,
Dimitar
A custom type does not implicitly define an ordering between its instances:
>>> v1 = Vertex(1)
>>> v2 = Vertex(2)
>>> v1 < v2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Vertex() < Vertex()
You need to tell python how to compare your vertices. You need to implement rich comparison methods to do so. Since heapify requires <, you must at least implement __lt__.
You can also have a look at the #total_ordering decorator to avoid implementing all of them.
Something along the lines of:
from functools import total_ordering
#total_ordering
class Vertex:
# Your current class code
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.distance == other.distance
return NotImplemented
def __lt__(self, other):
if isinstance(other, self.__class__):
return self.distance < other.distance
return NotImplemented
And if you need to use that class as a dictionary key, you’ll need to add the __hash__ method. Probably like so:
def __hash__(self):
return id(self)

How do I delete an edge from a graph in Python

I'm reading ThinkComplexity book, I'm new to python, so I have this code:
class Graph(dict):
def __init__(self, vs=[], es=[]):
"""
:param vs: list of vertices/nodes in the graph
:param es: list of edges/connection for the nodes
:return: Object graph
"""
for v in vs:
self.add_vertex(v) #also node
for e in es:
self.add_edge(e) #arc/edge/line
def add_vertex(self, v):
"""
:param v: Add the node/vertex to the graph
:return: Nothing just add
"""
self[v] = {}
def add_edge(self, e):
"""
:param e: Add arc/edge/line to the graph here is in both directions as it is undirected graph, if there is a arc already replace it
:return: Nothing just add
"""
v, w = e
self[v][w] = e
self[w][v] = e
def get_edge(self, v1, v2):
try:
if self != None:
if self[v1][v2] == self[v2][v1]:
print 'got it'
return True
except:
return None
def remove_edge(self, e, e2):
try:
if self != None:
del self[e][e2]
del self[e2][e]
print 'deleted\n', e[0], e[1]
return True
except:
return None
def vertices(self): #get the list of nodes
nodes = []
for node in self.keys():
nodes.append(node.label)
print nodes, '\n'
return nodes
def edges(self):
list_edges = []
count = 0
for node in self:
for edges in self[node]:
count += 1
print self[node].values(), count
list_edges.append(self[node].values())
return list_edges
def out_vertices(self, v): #nodes connected to this node
connected = []
for node in v.keys():
connected.append(node)
print node, 'this node is connected to', v.keys()
return connected
def out_edges(self, v):
list_out_edges = []
for ed in v.values():
print ed, 'edges from to'
list_out_edges.append(ed)
return list_out_edges
class Vertex(object): #nodes fro the graph
def __init__(self, label=''):
self.label = label
def __repr__(self):
return 'Vertex/Node(%s)' % repr(self.label)
__str__= __repr__
class Edge(tuple):
def __new__(cls, e1, e2):
return tuple.__new__(cls, (e1, e2))
def __repr__(self):
return 'Edge(%s---%s) <-undirected' % (repr(self[0]), repr(self[1]))
__str__ = __repr__
In chapter 2 4. Write a method named remove_edge that takes an edge and removes all references to it from the graph.
I have the get_edge() code done, but t remove I don't know how to do it, any help?? This would be an starting point to do a Bayes Network, so I want to learn about python and graphs. Thanks
Like this:
def remove_edge(g, e, e2):
try:
if g != None:
del g[e][e2]
del g[e2][e]
print 'deleted\n', e[0], e[1]
return True
except:
return None
The keys in the dictionary can be objects, no only str. And need to do it both ways because is undirected

Categories