Error while running a function more than a single time - python

The function only executes single time if I call it again it throws the keyError exception.
Function Call
Dijkstra.shortest_path(network_graph, 'Delhi', 'Kochin')
Dijkstra.shortest_path(network_graph, 'Chandigarh', 'Kolkata')
Function
def shortest_path(graph, src, dest) :
#print(src,dest)
shortest_distance = {}
visited = {}
unvisited = graph
path = []
for vertex in unvisited :
shortest_distance[vertex] = float('inf')
shortest_distance[src] = 0
while unvisited:
mincostV = None
for vertex in unvisited:
if mincostV is None:
mincostV = vertex
elif shortest_distance[vertex] < shortest_distance[mincostV]:
mincostV = vertex
for neighbour, weight in unvisited[mincostV].items():
if weight + shortest_distance[mincostV] < shortest_distance[neighbour]:
shortest_distance[neighbour] = weight + shortest_distance[mincostV]
visited[neighbour] = mincostV
unvisited.pop(mincostV)
currentVertex = dest
while currentVertex != src:
path.insert(0, currentVertex)
currentVertex = visited[currentVertex]
path.insert(0, src)
if shortest_distance[dest] != float('inf'):
#print('Shortest Distance is',shortest_distance[dest],'kms.')
print('via', path)
return path
The function shortest_path returns the shortest path between to vertices of a graph using Dijkstra's SPSP algorithm. I want to find the shortest path between multiple vertices, it is working only single time and throwing keyError exception when called with different parameters.

Related

my implementation of dijkstra algorithm in python using recursion

i'm beginner and this my implementation can you give you opinion about this algorithm .
I managed to build this algorithm after 3 days
I found lots of implementation but i found them confusing .
and I wanted to build it by myself.
class Graph:
def __init__(self, size):
self.edges = {}
self.cost = {}
def addNode(self, node):
self.edges[node] = {}
def addEdge(self, node1, node2,w):
self.edges[node1][node2] = w
def getSub(self, node):
return self.edges[node]
def getCost(self,node):
return self.cost[node]
def setCost(self, node1, node2, edge):
if self.getCost(node1) + edge < self.getCost(node2):
self.cost[node2] = self.getCost(node1) + edge
def getDistance(self,node1, node2,c):
return self.getSub(node1)[node2]+c
# this function travel all graph and update cost of each node
def Dijkstra(self, start, visited):
visited +=[start]
for child in self.getSub(start):
self.setCost(start, child, self.getSub(start)[child])
for node in self.getSub(start):
if node not in visited:
self.Dijkstra(node,visited)
# after we set cost for each node/ver we need this fun to find small dis
def Dijkstra_helper(self, start, end, paths, dis = 0):
paths += [start]
if start == end:
return paths
for node in self.getSub(start):
if end in paths:
break
if node not in paths:
new_dis = self.getDistance(start, node, dis)
"""
S -- 1-- c
\2 this just part of graph example if we come from A. the distance from A to B is 6 > 3 that's mean we should
A--6-->B(3) come from C if statement prevent as from continue in this path
"""
if new_dis <= self.getCost(node) and new_dis <=self.getCost(end):
self.Dijkstra_helper(node, end, paths, new_dis)
return paths
if __name__ == "__main__":
nodes = ["S","B","C","D",'E']
g = Graph(len(nodes))
for node in nodes:
g.addNode(node)
g.cost["S"] = 0
infinity = float('inf')
for n in nodes[1:]:
g.cost[n] = infinity
g.addEdge("S", "D",1)
g.addEdge('S', "B",6)
g.addEdge("B", "C",5)
g.addEdge("D", "E",1)
g.addEdge("D", "B",2)
g.addEdge("E", "B",2)
g.addEdge("E", "C",5)
g.Dijkstra("S",[])
print(g.cost)
print(g.Dijkstra_helper("S","C",[]))
I tested this algorithm in it works .but there's only one problem the shortest path depend on how you build your graph like if you put S--> D if the first it not like hen you put S --> B you can test the algorithm To understand what I want to say. Is there any way I could further optimize it?

Solving Dijkstra's algorithm - passing costs / parents with two edges

I have a graph like this:
# graph table
graph = {}
graph['start'] = {}
graph['start']['a'] = 5
graph['start']['b'] = 2
graph['a'] = {}
graph['a']['c'] = 4
graph['a']['d'] = 2
graph['b'] = {}
graph['b']['a'] = 8
graph['b']['d'] = 7
graph['c'] = {}
graph['c']['d'] = 6
graph['c']['finish'] = 3
graph['d'] = {}
graph['d']['finish'] = 1
graph['finish'] = {}
And I am trying to find the fastest way from S to F.
In the first example in the book only one edge was connected to one node, in this example for example, node D has 3 weights and a cost table was used:
costs = {}
infinity = float('inf')
costs['a'] = 5
costs['b'] = 2
costs['c'] = 4
costs['d'] = # there is 3 costs to node D, which one to select?
costs['finish'] = infinity
And a parents table:
parents = {}
parents['a'] = 'start' # why not start and b since both `S` and `B` can be `A` nodes parent?
parents['b'] = 'start'
parents['c'] = 'a'
parents['d'] = # node D can have 3 parents
parents['finish'] = None
But this also works, by works I mean no error is thrown, so do I only have to name the parents from the first node S?
parents = {}
parents['a'] = 'start'
parents['b'] = 'start'
parents['finish'] = None
The code:
processed = []
def find_lowest_cost_node(costs):
lowest_cost = float('inf')
lowest_cost_node = None
for node in costs:
cost = costs[node]
if cost < lowest_cost and node not in processed:
lowest_cost = cost
lowest_cost_node = node
return lowest_cost_node
node = find_lowest_cost_node(costs)
while node is not None:
cost = costs[node]
neighbors = graph[node]
for n in neighbors.keys():
new_cost = cost + neighbors[n]
if costs[n] > new_cost:
costs[n] = new_cost
parents[n] = node
processed.append(node)
node = find_lowest_cost_node(costs)
def find_path(parents, finish):
path = []
node = finish
while node:
path.insert(0, node)
if parents.__contains__(node):
node = parents[node]
else:
node = None
return path
path = find_path(parents, 'finish')
distance = costs['finish']
print(f'Path is: {path}')
print(f'Distance from start to finish is: {distance}')
I get:
Path is: ['finish']
Distance from start to finish is: inf
Where is my mistake and how should I add cost and parent to a node which can be visited from more than 1 node?
Edit
I do believe this is not the best approach for this problem, the best in practice solution / recommendations are welcome.
You do not need to initialise the cost table with more than costs['start'] = 0 or the parents dictionary with more than parents = {}. That is what your algorithm is going to create for you!
The only other change you need to make is to your while loop. It just needs to check whether the new node has already been detected before. If so then we check to see whether the new path is shorter and update as required; if not then we establish the new path.
while node is not None:
cost = costs[node]
neighbors = graph[node]
for n in neighbors.keys():
new_cost = cost + neighbors[n]
if n in costs:
if costs[n] > new_cost:
costs[n] = new_cost
parents[n] = node
else:
costs[n] = new_cost
parents[n] = node
processed.append(node)
node = find_lowest_cost_node(costs)
I think there are much neater ways to deal with graphs but this is the minimal change needed to make your code work as required. Hope it's helpful!

Store all paths from start to end node using BFS

I have the implemetation of BFS but it stores only one path. How can I modify this code to store all paths from a starting to node to the end. Any ideas?
def BFS(G, user1, user2):
path = []
for v in G:
v.setDistance(0)
v.setPred(None)
vertQueue = Queue()
vertQueue.enqueue(G.getVertex(user1))
while vertQueue.size() > 0:
currentVert = vertQueue.dequeue()
for nbr in currentVert.getConnections():
if nbr.getColor() == 'white':
nbr.setColor('gray')
nbr.setDistance(currentVert.getDistance() + 1)
nbr.setPred(currentVert)
vertQueue.enqueue(nbr)
currentVert.setColor('black')
prev = G.getVertex(user2)
while prev.getPred():
path.append(prev.getPred().getId())
prev = prev.getPred()
path = path[::-1]
path.append(user2)
return ' -> '.join(path)

Object is not callable?

Hi everyone I cannot figure out how to fix this issue and from my knowledge it's because of the attributes of the object's I am using.
For your information, I am making a graph in Python and the graph needs to check if all the vertices and edges are connected.
class graph(object):
def __init__(self, gdict=None):
if gdict == None:
gdict = {}
self.gdict = gdict
# return the keys of the dictionary list, or vertices
def getVertice(self):
return list(self.gdict.keys())
# this allows us to obtain the edges for the graph
# or "values" of the dict key
def getEdges(self):
return self.generate_edges()
def addVertex(self, vertex):
if vertex not in self.gdict:
self.gdict[vertex] = []
def addEdge(self, edge):
edge = set(edge)
(vertex1, vertex2) = tuple(edge)
if vertex1 in self.gdict:
self.gdict[vertex1].append(vertex2)
else:
self.gdict[vertex1] = [vertex2]
# this generates a list of all edges for the vertices
def generate_edges(self):
edges = []
for vertex in self.gdict:
for neighbour in self.gdict[vertex]:
if (neighbour, vertex) not in edges:
edges.append((vertex, neighbour))
return edges
def find_path(self, startVertex, endVertex, paths=None):
if paths == None:
paths = []
graphs = self.gdict
paths = paths + [startVertex]
if startVertex == endVertex:
return paths
if startVertex not in graphs:
return None
for vertex in graphs[startVertex]:
if vertex not in paths:
extendedPath = self.find_path(vertex, endVertex, paths)
if extendedPath:
return extendedPath
return None
def findAllPath(self, startVertex, endVertex, paths=None):
if paths is None:
paths = []
graphs = self.gdict
paths = paths + [startVertex]
if startVertex == endVertex:
return [paths]
if startVertex not in graphs:
return []
paths = []
for vertex in graphs[startVertex]:
if vertex not in paths:
extendedPath = self.find_path(vertex, endVertex, paths)
for p in extendedPath:
paths.append(p)
return paths
def findisovertices(self): ##reword this
""" returns a list of isolated vertices. """
graphs = self.gdict
iso = []
for vertex in graphs:
print(iso, vertex)
if not graphs[vertex]:
iso += [vertex]
return iso
def isConnected(self, verticesMet=None, startVertex=None):
if verticesMet is None:
verticesMet = set()
gdict = self.gdict
vertices = self.gdict()
if not startVertex:
startVertex = vertices[0]
verticesMet.add(startVertex)
if len(verticesMet) != len(vertices):
for vertex in gdict[startVertex]:
if vertex not in verticesMet:
if self.isConnected(verticesMet, vertex):
return True
else:
return True
return False
# this function prints the nodes/vertices in the graph
def completeGraph(self):
Vertex = len(self.gdict.keys())
Edges = len(self.gdict.values())
answer = 2.0 * Edges / (Vertex * (Vertex - 1))
return answer
graph_elements = ({"a": ["d", "f"],
"b": ["c"],
"c": ["b", "c", "d", "e"],
"d": ["a", "c"],
"e": ["c"],
"f": ["a"],
"z": []
})
g = graph(graph_elements)
print("Our vertices are: \n", g.getVertice())
print("#1 | Generate list of all edges: \n", graph.generate_edges(g))
##2 Function to calculate isolated nodes of graph.
isolated = graph.findisovertices(g)
print("#2 | Find isolated nodes:\n", isolated)
# 3. Function to find a path from a start vertex to an end vertex
path = graph.find_path(g, "a", "c")
print("#3 | Find a path function: \n", path)
# 4. Function to find all the paths between a start vertex to an end vertex
allPaths = graph.findAllPath(g, "a", "e")
print("#4 | All paths function:\n", allPaths)
# 5. Function to check if graph is connected
connect = graph(g)
print("#5 | Connected graph function \n", connect.isConnected(g))
and I keep receiving the following error:
Traceback (most recent call last):
File "graphsAssign6.py", line 160, in <module>
print("#5 | Connected graph function \n", connect.isConnected(g))
File "graphsAssign6.py", line 95, in isConnected
vertices = self.gdict()
TypeError: 'graph' object is not callable
def isConnected(self, verticesMet=None, startVertex=None):
if verticesMet is None:
verticesMet = set()
gdict = self.gdict
vertices = self.getVertice()
if not startVertex:
startVertex = vertices[0]
verticesMet.add(startVertex)
if len(verticesMet) != len(vertices):
for vertex in gdict[startVertex]:
if vertex not in verticesMet:
if self.isConnected(verticesMet, vertex):
return True
else:
return True
return False
# 5. Function to check if graph is connected
print("#5 | Connected graph function \n", g.isConnected())
Don't make a new connect = graph(g). Your isConnected should work within g. Also, you shouldn't get your vertices with self.gdict(). It doesn't make sense and you already have a function named getVertice for that job.

Python - Dijsktra's Algorithm Distance Problem

I've run into a problem with my code, i'm not able to calculate the distance to a node from the starting node. I have a text file of the form:
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
This represents the node distances in the graph. Here is my code, unfortunately, despite trying a few different methods I still keep coming up with various error messages.
infinity = 1000000
invalid_node = -1
startNode = 0
class Node:
distFromSource = infinity
previous = invalid_node
visited = False
def populateNodeTable():
nodeTable = []
index =0
f = open('route.txt', 'r')
for line in f:
node = map(int, line.split(','))
nodeTable.append(Node())
print nodeTable[index].previous
print nodeTable[index].distFromSource
index +=1
nodeTable[startNode].distFromSource = 0
return nodeTable
def tentativeDistance(currentNode, nodeTable):
nearestNeighbour = []
for currentNode in nodeTable:
# if Node[currentNode].distFromSource + currentDistance = startNode + currentNode
# currentDistance = currentNode.distFromSource + nodeTable.currentNode
currentNode.previous = currentNode
currentNode.length = currentDistance
currentNode.visited = True
currentNode +=1
nearestNeighbour.append(currentNode)
print nearestNeighbour
return nearestNeighbour
def shortestPath (nearestNeighbour)
shortestPath = []
f = open ('spf.txt', 'r')
f.close()
currentNode = startNode
if __name__ == "__main__":
populateNodeTable()
tentativeDistance(currentNode,populateNodeTable())
The lines starting with '#' in my tentativeDistance function is the section giving me trouble. I've looked at some other implementations on the web though they confuse me
I have been programming the Dijkstra's Algorithm in Python a few months ago; its tested and it should work:
def dijkstra(u,graph):
n = graph.numNodes
l = { u : 0 } ; W = graph.V()
F = [] ; k = {}
for i in range(0,n):
lv,v = min([ (l[lk],lk) for lk in l.keys() if lk in W ])
W.remove(v)
if v!=u: F.append(k[v])
for v1 in [ v2 for v2 in graph.G(v) if v2 in W ]:
if v1 not in l or l[v]+graph.w(v,v1) < l[v1]:
l[v1] = l[v] + graph.w(v,v1)
k[v1] = (v,v1)
return l,F
You need a class Graph with Method V() (which yields the graphs nodes), w(v1,v2) (which yields the weight of the edge (v1,v2)), remove (which removes an edge from a graph) and attribute numNodes (which yields the number of nodes in the graph) and G(v) which yields the neighborhood of node v.

Categories