I have to calculate the centrality degree on a graph. My implementation is:
import csv
class Graph:
'''
Representation of a simple graph using an adjacency map.
There exist nested Classes for Vertex and Edge objects and
useful methods for vertex and edge, edge incidence and
vertex degree retrieval plus edge and vertex insertion
'''
# == Class Vertex == #
class Vertex:
'''
Class for representing vertex structure for a graph.
'''
__slots__ = '_element'
def __init__(self, x):
'''
Do not call constructor directly. Use Graph's insert_vertex(x).
'''
self._element = x
def element(self):
'''
Return element associated with this vertex.
'''
return self._element
def __hash__(self):
'''
will allow vertex to be a map/set key
'''
return hash(self._element)
def __repr__(self):
return '{0}'.format(self._element)
def __eq__(self, other):
if isinstance(other, Graph.Vertex):
return self._element == other._element
return False
# == Class Edge == #
class Edge:
'''
Class for representing edge structure for a graph.
'''
__slots__ = '_origin', '_destination', '_weight'
def __init__(self, u, v, x):
'''
Do not call constructor directly. Use Graph's insert_edge(x).
'''
self._origin = u
self._destination = v
self._weight = x
def endPoints(self):
'''
Return (u,v) tuple for vertices u and v.
'''
return (self._origin, self._destination)
def opposite(self, v):
'''
Return the vertex that is opposite v on this edge.
'''
return self._destination if self._origin == v else self._origin
def element(self):
'''
Return element associated with this edge.
'''
return self._weight
def __hash__(self):
'''
will allow edge to be a map/set key
'''
return hash((self._origin, self._destination))
def __repr__(self):
if self._weight is None:
return '({0}, {1})'.format(self._origin, self._destination)
return '({0}, {1}, {2})'.format(self._origin, self._destination, self._weight)
# == Class Graph == #
def __init__(self, directed=False):
'''
Create an empty graph (undirected, by default).
Graph is directed if optional parameter is set to True.
'''
self._outgoing = {}
self._incoming = {} if directed else self._outgoing
def __getitem__(self, arg):
return self._incoming[arg]
def is_directed(self):
'''
Return True if graph is directed
'''
return self._outgoing is not self._incoming
def vertex_count(self):
'''
Return the vertices count
'''
return len(self._outgoing)
def vertices(self):
'''
Return an iterator over the graph's vertices
'''
return self._outgoing.keys()
def get_vertex(self, el):
'''
Return the graph's vertex with corresponding element
equal to el. Return None on failure
'''
for vertex in self.vertices():
if vertex.element() == el:
return vertex
return None
def edges_count(self):
'''
Return the edges count
'''
edges = set()
for secondary_map in self._outgoing.values():
edges.update(secondary_map.values())
return len(edges)
def edges(self):
'''
Return a set of graph's edges
'''
edges = set()
for secondary_map in self._outgoing.values():
edges.update(secondary_map.values())
return edges
def get_edge(self, u, v):
'''
Return the edge from u to v
'''
return self._outgoing[u].get(v)
def degree(self, v, outgoing=True):
'''
Return the number of incident vertices to v
If graph is directed then handle the case of indegree
'''
inc = self._outgoing if outgoing else self._incoming
return len(inc[v])
def incident_edges(self, v, outgoing=True):
'''
Return all incident edges to node v.
If graph is directed, handle the case of incoming edges
'''
inc = self._outgoing if outgoing else self._incoming
if v not in inc:
return None
for edge in inc[v].values():
yield edge
def adjacent_vertices(self, v, outgoing=True):
'''
Return adjacent vertices to a given vertex
'''
if outgoing:
if v in self._outgoing:
return self._outgoing[v].keys()
else:
return None
else:
if v in self._incoming:
return self._incoming[v].keys()
else:
return None
def insert_vertex(self, x=None):
'''
Insert and return a new Vertex with element x
'''
for vertex in self.vertices():
if vertex.element() == x:
# raise exception if vertice exists in graph
# exception can be handled from the class user
return vertex
v = self.Vertex(x) # cria um objeto do tipo Vertex
self._outgoing[v] = {}
if self.is_directed:
self._incoming[v] = {}
return v
def insert_edge(self, u, v, x=None):
'''
Insert and return a new Edge from u to v with auxiliary element x.
'''
if (v not in self._outgoing) or (v not in self._outgoing):
# raise exception if one of vertices does not exist
# exception can be handled from the class user
raise Exception('One of the vertices does not exist')
if self.get_edge(u, v):
# no multiple edges
# exception can be handled from the class user
e = self.Edge(u, v, x)
return e
e = self.Edge(u, v, x) # cria um objeto do tipo Edge
self._outgoing[u][v] = e
self._incoming[v][u] = e
return e
def remove_edge(self, u, v):
if not self.get_edge(u, v):
# exception for trying to delete non-existent edge
# can be handled from class user
raise Exception('Edge is already non-existent.')
u_neighbours = self._outgoing[u]
del u_neighbours[v]
v_neighbours = self._incoming[v]
del v_neighbours[u]
def remove_vertex(self, x):
'''
Delete vertex and all its adjacent edges from graph
'''
if (x not in self._outgoing) and (x not in self._incoming):
raise Exception('Vertex already non-existent')
secondary_map = self._outgoing[x]
for vertex in secondary_map:
# delete reference to incident edges
if self.is_directed():
del self._incoming[vertex][x]
else:
del self._outgoing[vertex][x]
# delete reference to the vertex itself
del self._outgoing[x]
def printG(self):
'''Mostra o grafo por linhas'''
print('Grafo Orientado:', self.is_directed())
'''Mostra o número de vertices'''
print("Número de Vertices: {}".format(G.vertex_count()))
'''Mostra o número de arestas'''
print("Número de Arestas: {}".format(G.edges_count()))
for v in self.vertices():
print('\nUser: ', v, ' grau_in: ', self.degree(v, False), end=' ')
if self.is_directed():
print('grau_out: ', self.degree(v, False))
for i in self.incident_edges(v):
print(' ', i, end=' ')
if self.is_directed():
for i in self.incident_edges(v, False):
print(' ', i, end=' ')
My graph is constructed from a CSV file:
def read_csv(filename):
G = Graph() # cria um objeto do tipo Graph
with open(filename, 'r') as csv_file: # abre o ficheiro csv
data = csv.reader(csv_file)
next(data) # ignora a primeira coluna do ficheiro
for linha in data: # por cada linha no ficheiro
id_origem = linha[0] # a origem é a primeira coluna do ficheiro
id_destino = linha[1] # o destino é a segunda coluna do ficheiro
peso = linha[2] if len(linha) > 2 else 1 # se não existir uma terceira coluna do ficheiro
# assume-se que o peso das arestas, é 1
v_origem = G.insert_vertex(id_origem) # insere o vertex no grafo
v_destino = G.insert_vertex(id_destino) # insere o vertex no grafo
G.insert_edge(v_origem, v_destino, int(peso)) # insere a aresta no grafo
return G
CSV file has the structure:
follower,followed,distance
Murphy,Thornton,45
Perkins,Walters,26
Perkins,Bradley,7
Lawrence,Hart,15
The shortest distance are calculated by:
def shortest_path_lengths(g, src):
d = {}
cloud = {}
pq = AdaptableHeapPriorityQueue()
pqlocator = {}
source = Graph.Vertex(src)
for v in G.vertices():
if v == source:
d[v] = 0
else:
d[v] = float('inf')
pqlocator[v] = pq.add(d[v], v)
while not pq.is_empty():
key, u = pq.remove_min()
cloud[u] = key
del pqlocator[u]
for e in G.incident_edges(u):
v = e.opposite(u)
if v not in cloud:
wgt = e.element()
if d[u] + wgt < d[v]:
d[v] = d[u] + wgt
pq.update(pqlocator[v], d[v], v)
return cloud
I'm calculating the centrality degree from:
def centrality_degree(G, src=None):
distances = []
closeness_centr = []
short = shortest_path_lengths(G, src)
#print(short)
vertex_number = G.vertex_count()
#while contador <= len(short):
#for vertex in G.vertices(): #G.adjacent_vertices(G.get_vertex(src)):
for value in short.values():
if value != float('inf'):
distances.append(value)
print(distances)
soma = sum(distances)
#print(soma)
formula = (vertex_number-1)/soma
closeness_centr.append(formula)
print(closeness_centr)
Is there any way to caculate the centrality degree without passing the parameter src?
Try:
def centrality_degree(G):
n_vertex = ...
# compute all the shortest paths from the node i to j
# (do a for in a for)
degree = ...
return degree
Related
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
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]
I'm trying to run this code but I get AttributeError:
File "D:/QGIS TRAINING/Masir/dkj.py", line 98, in
print(graph.dijkstra("905577", "703920")) AttributeError: 'list' object has no attribute 'dijkstra'
I make list from a csv file and set it as an input to my algorithm
here is my code
import csv
from collections import deque, namedtuple
with open('D:/QGIS TRAINING/Masir/yal zero.csv', 'r') as f:
reader = csv.reader(f)
list_nodes = list(reader)
# we'll use infinity as a default distance to nodes.
inf = float('inf')
Edge = namedtuple('Edge', 'start, end, cost')
def make_edge(start, end, cost=1):
return Edge(start, end, cost)
class Graph:
def __init__(self, edges):
# let's check that the data is right
wrong_edges = [i for i in edges if len(i) not in [2, 3]]
if wrong_edges:
raise ValueError('Wrong edges data: {}'.format(wrong_edges))
self.edges = [make_edge(*edge) for edge in edges]
#property
def vertices(self):
return set(
sum(
([edge.start, edge.end] for edge in self.edges), []
)
)
def get_node_pairs(self, n1, n2, both_ends=True):
if both_ends:
node_pairs = [[n1, n2], [n2, n1]]
else:
node_pairs = [[n1, n2]]
return node_pairs
def remove_edge(self, n1, n2, both_ends=True):
node_pairs = self.get_node_pairs(n1, n2, both_ends)
edges = self.edges[:]
for edge in edges:
if [edge.start, edge.end] in node_pairs:
self.edges.remove(edge)
def add_edge(self, n1, n2, cost=1, both_ends=True):
node_pairs = self.get_node_pairs(n1, n2, both_ends)
for edge in self.edges:
if [edge.start, edge.end] in node_pairs:
return ValueError('Edge {} {} already exists'.format(n1, n2))
self.edges.append(Edge(start=n1, end=n2, cost=cost))
if both_ends:
self.edges.append(Edge(start=n2, end=n1, cost=cost))
#property
def neighbours(self):
neighbours = {vertex: set() for vertex in self.vertices}
for edge in self.edges:
neighbours[edge.start].add((edge.end, edge.cost))
return neighbours
def dijkstra(self, source, dest):
assert source in self.vertices, 'Such source node doesn\'t exist'
distances = {vertex: inf for vertex in self.vertices}
previous_vertices = {
vertex: None for vertex in self.vertices
}
distances[source] = 0
vertices = self.vertices.copy()
while vertices:
current_vertex = min(
vertices, key=lambda vertex: distances[vertex])
vertices.remove(current_vertex)
if distances[current_vertex] == inf:
break
for neighbour, cost in self.neighbours[current_vertex]:
alternative_route = distances[current_vertex] + cost
if alternative_route < distances[neighbour]:
distances[neighbour] = alternative_route
previous_vertices[neighbour] = current_vertex
path, current_vertex = deque(), dest
while previous_vertices[current_vertex] is not None:
path.appendleft(current_vertex)
current_vertex = previous_vertices[current_vertex]
if path:
path.appendleft(current_vertex)
return path
graph = list_nodes
print(graph.dijkstra("905577", "703920"))
is it something wrong with my list or with the algorithm?
There's the problem with the way you instantiate the graph object. You see, this:
graph = list_nodes
is a list of nodes from list_nodes = list(reader).
But this:
graph = Graph(list_nodes)
is probably what you actually want.
Just in case, here's a DigitalOcean tutorial on object construction
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
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