I have written this code. This is a code of Uniform COst search. I have to find the path between Arad and Bucharest. My problem is that my code is giving the correct total cost that is 418. But I can not figure out how to find the path that is giving this cost. Any help is appreciated.
from queue import PriorityQueue
class Graph:
def __init__(self):
self.edges={"Arad":["Zerind","Timisoara","Sibiu"],"Zerind":["Oradea"],"Oradea":["Sibiu"],"Timisoara":["Lugoj"],"Lugoj":["Mehadia"],"Mehadia":["Dobreta"],"Dobreta":["Craiova"],"Sibiu":["Fagaras","RimnicuVilcea"],"Craiova":["RimnicuVilcea","Pitesti"],"RimnicuVilcea":["Craiova","Pitesti"],"Fagaras":["Bucharest"],"Pitesti":["Bucharest"],"Bucharest":["Giurgiu","Urziceni"],"Urziceni":["Hirsova","Vaslui"],"Hirsova":["Eforie"],"Vaslui":["Lasi"],"Lasi":["Neamt"]}
self.weights={"AradZerind":75,"ZerindOradea":71,"AradTimisoara":118,"TimisoaraLugoj":111,"LugojMehadia":70,"MehadiaDobreta":75,"AradSibiu":140,"OradeaSibiu":151,"DobretaCraiova":120,"CraiovaRimnicuVilcea":146,"CraiovaPitesti":138,"SibiuFagaras":99,"SibiuRimnicuVilcea":80,"RimnicuVilceaPitesti":97,"RimnicuVilceaCraiova":146,"FagarasBucharest":211,"PitestiBucharest":101,"BucharestGiurgiu":90,"BucharestUrziceni":85,"UrziceniHirsova":98,"HirsovaEforie":86,"UrziceniVaslui":142,"VasluiLasi":92,"LasiNeamt":87}
def neighbors(self,node):
return self.edges[node]
def get_cost(self,from_node,to_node):
return self.weights[(from_node + to_node)]
def ucs(graph, start, goal):
global total_cost
visited = set()
path=[]
queue = PriorityQueue()
queue.put((0, start))
while queue:
cost, node = queue.get()
if node not in visited:
visited.add(node)
if node == goal:
return visited
for i in graph.neighbors(node):
if i not in visited:
total_cost = cost + graph.get_cost(node, i)
queue.put((total_cost, I)
graph=Graph()
s=ucs(graph,"Arad","Bucharest")
print(s)
You can use initialize your (priority) queue like this:
queue = PriorityQueue()
queue.put([0,[start]])
Here, start is a tuple representing the starting state or anything you want to represent in your way.
Then unpack it inside the while loop:
cost,path = queue.get()
x,y=path[-1]
You don't need to define the path var in advance.
When the goal state is reached, instead of returning the cost, just print(cost) or whatever var you want to print and return the path:
x,y=path[-1]
And, to update it when we are traversing the adjacency list of each node, you can do this:
queue.put([costx,path + [(x2, y2)]])
If you want to keep track of many things, you can keep it inside the 'priorityQueue() (your queue).
I can add the code if you want but maybe that won't be necessary.
Related
I am trying to create a python class that establishes a grid of n x n numbers and then uses that grid for the purpose of depth-first and breadth-first search/traversal, based on a starting point in the grid. The variables that would be defined when calling the class as part of a test are the starting row + the starting column (defining the start point) and the size of the row + size of the column (defining the two dimensional grid space). the class starts like this:
class Graph:
def __init__(self, start_row: int, start_col: int, size_row: int, size_col: int):
#constructor
self.graph = [[0] * size_col for _ in range(size_row)]
self.bfs(start_row, start_col)
I was able to create the matrix based on what the inputs would be included while calling the object, which would look something like this:
def test_graph():
g = Graph(3, 5, 8, 9)
The remainder of the code for the Breadth-First Search algorithm looks like this, in the same class built as another method:
BFS function
def bfs(self,s): # this is our function using params as
# visited nodes, the graph, and the node
# initialize all vertices as not visited
visited = [False] * (max(self.graph)+1)
# create a BFS queue
queue = []
# mark the source node as visited and enqueue it
queue.append(s)
visited[s] = True
# while loop to keep the routine running until nothing
#left to visit
while queue:
# remove a vertex from queue
s = queue.pop()
# get all adjacent vertices of the last considered
#vertex; if adjacency not visited, mark
# ~ it visited and enqueue
for i in self.graph[s]:
if visited[i] == False:
queue.append[i]
visited[i] = True
The problem I am having is that my object is returning arrays of all zeroes as opposed to the outcome of the BFS. Am I missing something in my Class that is causing the object to not execute the BFS method on the given Class inputs?
Multiple issues in this
Calling self.bfs() with 2 arguments (row and col) while the definition has only one (s)
max(self.graph) would return a list, you can't initialize visited with that.
s=queue.pop() will actually make the function behave as dfs and not bfs. .pop() pops from the end of the list. If you have [1,2,3] and call pop(), 3 will be returned and behave as a stack. To make it behave like a queue, queue.pop(0) should be used.
Not sure what the logic is behind using a single dimension visited array and what s refers to.
I've been trying to understand BFS and DFS better, and I was wondering if I could get some help on this: I want to return the list of visited nodes for both of them. Since it would be redundant to post snippets for each and ask the same question, I'll just go with BFS:
visited = [] # List to keep track of visited nodes.
queue = [] #Initialize a queue
def bfs(visited, graph, node):
visited.append(node)
queue.append(node)
while queue:
s = queue.pop(0)
print (s, end = " ")
for neighbour in graph[s]:
if neighbour not in visited:
visited.append(neighbour)
queue.append(neighbour)
This is from educative.io (python). Would I just delete the print function and add "return visited" at the very end? Thanks!
I have implemented a simple graph data structure in Python with the following structure below. The code is here just to clarify what the functions/variables mean, but they are pretty self-explanatory so you can skip reading it.
# Node data structure
class Node:
def __init__(self, label):
self.out_edges = []
self.label = label
self.is_goal = False
def add_edge(self, node, weight = 0):
self.out_edges.append(Edge(node, weight))
# Edge data structure
class Edge:
def __init__(self, node, weight = 0):
self.node = node
self.weight = weight
def to(self):
return self.node
# Graph data structure, utilises classes Node and Edge
class Graph:
def __init__(self):
self.nodes = []
# some other functions here populate the graph, and randomly select three goal nodes.
Now I am trying to implement a uniform-cost search (i.e. a BFS with a priority queue, guaranteeing a shortest path) which starts from a given node v, and returns a shortest path (in list form) to one of three goal node. By a goal node, I mean a node with the attribute is_goal set to true.
This is my implementation:
def ucs(G, v):
visited = set() # set of visited nodes
visited.add(v) # mark the starting vertex as visited
q = queue.PriorityQueue() # we store vertices in the (priority) queue as tuples with cumulative cost
q.put((0, v)) # add the starting node, this has zero *cumulative* cost
goal_node = None # this will be set as the goal node if one is found
parents = {v:None} # this dictionary contains the parent of each node, necessary for path construction
while not q.empty(): # while the queue is nonempty
dequeued_item = q.get()
current_node = dequeued_item[1] # get node at top of queue
current_node_priority = dequeued_item[0] # get the cumulative priority for later
if current_node.is_goal: # if the current node is the goal
path_to_goal = [current_node] # the path to the goal ends with the current node (obviously)
prev_node = current_node # set the previous node to be the current node (this will changed with each iteration)
while prev_node != v: # go back up the path using parents, and add to path
parent = parents[prev_node]
path_to_goal.append(parent)
prev_node = parent
path_to_goal.reverse() # reverse the path
return path_to_goal # return it
else:
for edge in current_node.out_edges: # otherwise, for each adjacent node
child = edge.to() # (avoid calling .to() in future)
if child not in visited: # if it is not visited
visited.add(child) # mark it as visited
parents[child] = current_node # set the current node as the parent of child
q.put((current_node_priority + edge.weight, child)) # and enqueue it with *cumulative* priority
Now, after lots of testing and comparing with other alogrithms, this implementation seemed to work pretty well - up until I tried it with this graph:
For whatever reason, ucs(G,v) returned the path H -> I which costs 0.87, as opposed to the path H -> F -> I, costing 0.71 (this path was obtained by running a DFS). The following graph also gave an incorrect path:
The algorithm gave G -> F instead of G -> E -> F, obtained again by the DFS. The only pattern I can observe among these rare cases is the fact that the chosen goal node always has a loop. I can't figure out what is going wrong though. Any tips will be much appreciated.
Usually for searches, I tend to keep the path to a node part of the queue. This is not really memory efficient, but cheaper to implement.
If you want the parent map, remember that it is only safe to update the parent map when the child is on top of the queue. Only then has the algorithm determined the shortest path to the current node.
def ucs(G, v):
visited = set() # set of visited nodes
q = queue.PriorityQueue() # we store vertices in the (priority) queue as tuples
# (f, n, path), with
# f: the cumulative cost,
# n: the current node,
# path: the path that led to the expansion of the current node
q.put((0, v, [v])) # add the starting node, this has zero *cumulative* cost
# and it's path contains only itself.
while not q.empty(): # while the queue is nonempty
f, current_node, path = q.get()
visited.add(current_node) # mark node visited on expansion,
# only now we know we are on the cheapest path to
# the current node.
if current_node.is_goal: # if the current node is a goal
return path # return its path
else:
for edge in in current_node.out_edges:
child = edge.to()
if child not in visited:
q.put((current_node_priority + edge.weight, child, path + [child]))
Note: I haven't really tested this, so feel free to comment, if it doesn't work right away.
A simple check before expanding the node can save you duplicate visits.
while not q.empty(): # while the queue is nonempty
f, current_node, path = q.get()
if current_node not in visited: # check to avoid duplicate expansions
visited.add(current_node) # mark node visited on expansion,
# only now we know we are on the cheapest path to
# the current node.
if current_node.is_goal: # if the current node is a goal
return path # return its path
...
I am trying to write an A* search to solve a maze in Python, however I am struggling to find a built in priority queue that works for this. I am using PriorityQueue at the moment, but it offers no functionality in order to change an items priority, which is a problem in the commented section at the bottom of the algorithm (in the else if statement).
Does anyone have any idea what I could do in that else if block, or what built in priority queue would give me this functionality?
def A_search(maze, start, end):
expanded = 0 # use to track number of nodes expanded by the algorithm
node1 = Node(start,0)
frontier = PriorityQueue()
frontier.put((dist_to_goal(node1,end) + node1.get_cost(), node1))
visited = []
in_frontier = [] # keep track of items in frontier, PriorityQueue has no way to peek
in_frontier.append(node1)
while(True):
if(frontier == []):
return(None,expanded)
curr = (frontier.get())[1]
in_frontier.remove(curr)
expanded += 1
if(curr.get_loc() == end):
return(curr,expanded)
visited.append(curr.get_loc())
neighbors = find_neighbors(maze, curr.get_loc())
for neighbor in neighbors:
node_n = Node(neighbor,node1.get_cost()+1)
node_n.parent = curr
if(neighbor not in visited) and (node_n not in in_frontier):
frontier.put((dist_to_goal(node_n,end) + node1.get_cost(), node_n))
in_frontier.append(node_n)
# else if node_n is in frontier w/ a higher path cost then replace it w/ current
The closest you will find in the built-in libraries is heapq.
After changing the priority you need to call either heapq.heapify (costs O(n) time but will not change A* overall complexity) or use the internal heapq._siftdown function at O(log n) time.
Updating the item priority, is discussed in the official python documentation about the heapq module in the priority queue implementation notes: https://docs.python.org/3.7/library/heapq.html#priority-queue-implementation-notes
Using these notes, I managed to write my own PriorityQueue Implementation that supports adding a task and updating it's priority if it exists. It consists in using an entry_finder dict which point to tasks in the priority queue. Updating a task's priority, simply consists in marking the existing task as deleted and inserting it with the new priority.
In this implementation, you can use method add_task
class PriorityQueue():
REMOVED = '<removed-task>'
def __init__(self):
self.pq = []
self.entry_finder = {}
self.counter = itertools.count()
def add_task(self, task, priority=0):
if task in self.entry_finder:
self.remove_task(task)
count = next(self.counter)
entry = [priority, count, task]
self.entry_finder[task] = entry
heappush(self.pq, entry)
def remove_task(self, task):
entry = self.entry_finder.pop(task)
entry[-1] = self.REMOVED
def pop_task(self):
while self.pq:
priority, count, task = heappop(self.pq)
if task is not self.REMOVED:
del self.entry_finder[task]
return task
return None
I am having some trouble in determining the distance of each node from the start node, or rather getting any information back at all.
I get no output from my function, attached in the following link.
#Values to assign to each node
class Node:
distFromSource = infinity
previous = invalid_node
visited = False
#for each node assign default values
def populateNodeTable(network):
nodeTable = []
index = 0
f = open('network.txt', 'r')
for line in f:
node = map(int, line.split(','))
nodeTable.append(Node())
print "The previous node is " ,nodeTable[index].previous
print "The distance from source is " ,nodeTable[index].distFromSource
index +=1
nodeTable[startNode].distFromSource = 0
return nodeTable
#calculate the distance of each node from the start node
def tentativeDistance(currentNode, nodeTable):
nearestNeighbour = []
for currentNode in nearestNeighbour:
currentDistance == currentNode.distFromSource + [currentNode][nearestNeighbour] #gets current distance from source
print "The current distance"
if currentDistance != 0 & currentNode.distFromSource < Node[currentNode].distFromSource:
nodeTable[currentNode].previous = currentNode
nodeTable[currentNode].length = currentDistance
nodeTable[currentNode].visited = True
nodeTable[currentNode] +=1
nearestNeighbour.append(currentNode)
for currentNode in nearestNeighbour:
print nearestNeighbour
return nearestNeighbour
My logic is, at least in my mind, correct; however, I don't get as much as an error message when the code is run.
You're setting nearestNeighbour to be an empty list, and then you're looping over it with for currentNode in nearestNeighbour -- which does nothing, because the list is empty -- and then you're returning from the function.
(I assume tentativeDistance is the function you're calling and seeing nothing from.)
You should rethink your algorithm design. Try looking up a pseudocode definition of Dijkstra's algorithm and implementing that in Python. In particular, you should think about the control flow in your program.
You might want to have a look at this cookbook recipe for a Python implementation of Dijkstra and see if you can understand it.