Problem Using PriorityQueue with Objects - Python - python

I'm trying to create a Uniform Cost Search algorithm. but I'm having a problem in storing nodes in the priorityqueue.
It works well till node D as shown in the output provided, and I'm not sure why. Any help will be appreciated.
The error says it can't compare nodes but I'm adding them as tuples so it can use the distacne for comparison
class GraphEdge(object):
def __init__(self, destinationNode, distance):
self.node = destinationNode
self.distance = distance
class GraphNode(object):
def __init__(self, val):
self.value = val
self.edges = []
def add_child(self, node, distance):
self.edges.append(GraphEdge(node, distance))
def remove_child(self, del_node):
if del_node in self.edges:
self.edges.remove(del_node)
class Graph(object):
def __init__(self, node_list):
self.nodes = node_list
def add_edge(self, node1, node2, distance):
if node1 in self.nodes and node2 in self.nodes:
node1.add_child(node2, distance)
node2.add_child(node1, distance)
def remove_edge(self, node1, node2):
if node1 in self.nodes and node2 in self.nodes:
node1.remove_child(node2)
node2.remove_child(node1)
from queue import PriorityQueue
def build_path(root_node, goal_node):
path = [goal_node]
add_parent(root_node, goal_node, path)
return path
def add_parent(root_node, node, path):
parent = node.parent
path.append(parent)
if parent == root_node:
return
else:
add_parent(root_node, parent, path)
def ucs_search(root_node, goal_node):
visited = set()
queue = PriorityQueue()
queue.put((0, root_node))
visited_order = []
while not queue.empty():
current_node_priority, current_node = queue.get()
visited.add(current_node)
visited_order.append(current_node.value)
print("current_node:", current_node.value)
if current_node == goal_node:
print(visited_order)
return current_node, build_path(root_node, goal_node)
for edge in current_node.edges:
child = edge.node
if child not in visited:
child.parent = current_node
print("child:", child.value)
queue.put(((current_node_priority + edge.distance), child))
node_u = GraphNode('U')
node_d = GraphNode('D')
node_a = GraphNode('A')
node_c = GraphNode('C')
node_i = GraphNode('I')
node_t = GraphNode('T')
node_y = GraphNode('Y')
graph = Graph([node_u, node_d, node_a, node_c, node_i, node_t, node_y])
graph.add_edge(node_u, node_a, 4)
graph.add_edge(node_u, node_c, 6)
graph.add_edge(node_u, node_d, 3)
graph.add_edge(node_d, node_c, 4)
graph.add_edge(node_a, node_i, 7)
graph.add_edge(node_c, node_i, 4)
graph.add_edge(node_c, node_t, 5)
graph.add_edge(node_i, node_y, 4)
graph.add_edge(node_t, node_y, 5)
goal, sequence = ucs_search(node_a, node_y)
Output:
current_node: A
child: U
child: I
current_node: U
child: C
child: D
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-52-2d575db64232> in <module>
19 graph.add_edge(node_t, node_y, 5)
20
---> 21 goal, sequence = ucs_search(node_a, node_y)
<ipython-input-51-b26ec19983b6> in ucs_search(root_node, goal_node)
36 child.parent = current_node
37 print("child:", child.value)
---> 38 queue.put(((current_node_priority + edge.distance), child))
39
~\AppData\Local\Continuum\anaconda3\lib\queue.py in put(self, item, block, timeout)
147 raise Full
148 self.not_full.wait(remaining)
--> 149 self._put(item)
150 self.unfinished_tasks += 1
151 self.not_empty.notify()
~\AppData\Local\Continuum\anaconda3\lib\queue.py in _put(self, item)
231
232 def _put(self, item):
--> 233 heappush(self.queue, item)
234
235 def _get(self):
TypeError: '<' not supported between instances of 'GraphNode' and 'GraphNode'

If two tuples in the queue have the same distance, the priority queue needs to tiebreak based on the priority value of the corresponding GraphNodes. Since the __lt__ function isn't defined for GraphNodes, this will cause an error. (The __lt__ function defines how two GraphNodes can be compared using the < operator.)
To resolve, define the __lt__ function for the GraphNode class. This is the function that Python calls when comparing two GraphNodes:
class GraphNode(object):
def __init__(self, val):
self.value = val
self.edges = []
def add_child(self, node, distance):
self.edges.append(GraphEdge(node, distance))
def remove_child(self, del_node):
if del_node in self.edges:
self.edges.remove(del_node)
def __lt__(self, other):
return self.value < other.value

Related

implementing __next__() in python linked list

I would like to retrieve a particular node in a linked list by iterating a specified number of times; for example, to retrieve the 4th node. By implementing __iter__(), I can iterate with a for loop, but I don't know how get a next() function to work. I've commented out my attempt at a next() function; when it is left in, I still get AttributeError: 'LinkedList' object has no attribute 'next'
EDIT: I would like the getNode(self, loc) to return a node at the specified location by calling next() if possible.
Here is the Node and LinkedList classes:
class Node:
def __init__(self, data = None):
self.data = data
self.next = None
def __repr__(self):
return str(self.data)
class LinkedList:
def __init__(self, nodes = None):
self.head = None
if nodes is not None:
node = Node(data=nodes.pop(0))
self.head = node
for elem in nodes:
node.next = Node(data=elem)
node = node.next
def __repr__(self):
node = self.head
nodes = []
while node is not None:
nodes.append(str(node.data))
node = node.next
nodes.append("None")
return " -> ".join(nodes)
def __iter__(self):
node = self.head
while node is not None:
yield node
node = node.next
# def __next__(self):
# return self.next
def getNode(self,loc):
cnt = 0
for i in self:
if cnt < loc:
cnt += 1
else:
break
return i
ll = LinkedList([1,2,3,4,5])
print(ll)
print(ll.getNode(3))
for i in range(3):
print(ll.next())
[OUTPUT]
I
1 -> 2 -> 3 -> 4 -> 5 -> None
4
Traceback (most recent call last):
File "/Users/jk/_python_source/misc_python/_mymisc/Leetcode work/LinkedList.py", line 53, in <module>
print(ll.next())
AttributeError: 'LinkedList' object has no attribute 'next'
You're doing way more work than you have to. Once you've implemented __iter__, the rest falls into place. You can use it to implement pretty much all your other functions, like get_node, __str__ or __repr__, etc.
class Node:
def __init__(self, value):
self.value = value
self.next = None
def __str__(self):
return str(self.value)
class LinkedList:
def __init__(self):
self.head = None
def add(self, value):
if self.head is None:
self.head = Node(value)
else:
for cursor in self:
pass
cursor.next = Node(value)
return self
def get_node(self, node_index):
for index, node in enumerate(self):
if index == node_index:
break
else:
return None
return node
def __str__(self):
return " -> ".join(map(str, self))
def __iter__(self):
cursor = self.head
while cursor is not None:
yield cursor
cursor = cursor.next
ll = LinkedList().add(1).add(2).add(3)
print(ll)
for node_index in 0, 1, 2, 3:
print("The node at index {} is {}".format(node_index, ll.get_node(node_index)))
Output:
1 -> 2 -> 3
The node at index 0 is 1
The node at index 1 is 2
The node at index 2 is 3
The node at index 3 is None
>>>
ThisgetNode()also works:
def getNode(self,loc):
it = iter(self)
for i in range(loc):
next(it)
return next(it)

Reshaping a Linked List

Hello I am working on an assignment and stuck with some implementations, the purpose of this is reshaping or modifying a linked list. As you can see from my drawings this will not work as a classical linked list, the drawing 1 is for the classical list appending and I want to do it like the second drawing. There is not a lot of information, similar code to the drawing 2 will help a lot.
class Node:
"""Node class for the linked list"""
def __init__(self, val: Any, next_node: "Node"):
self.val = val
self.next_node = next_node
def __repr__(self):
return f"({self.val})->{self.next_node}"
class LinkedList:
"""Linked list class"""
def __init__(self, iterable: Optional[Iterable] = None):
self.head = None
self._length = 0
if isinstance(iterable, (list, tuple, str)):
for item in iterable:
self.push(item)
elif iterable is not None:
raise TypeError("If providing an input, it must be an iterable.")
def __len__(self):
return self._length
def __repr__(self):
return self.display()
def push(self, val):
self.head = Node(val, self.head)
self._length += 1
def pop(self):
if not self.head:
raise IndexError("Cannot pop from an empty list.")
popped_value = self.head.val
self.head = self.head.next_node
self._length -= 1
return popped_value
def size(self):
return self._length
def search(self, val):
current_node = self.head
while current_node.val != val:
current_node = current_node.next_node
if current_node is None:
break
return current_node
def remove(self, val):
node = self.search(val)
if node is None:
raise ValueError("Value does not exist in list.")
node.val = node.next_node.val
node.next_node = node.next_node.next_node
self._length -= 1
return self
def display(self):
return f"({self.head.val})->{self.head.next_node}"
As can see, the node A is linked to B, node B linked to C, node C is linked toA. Okay, this segment is a circular list.
But, you add two node: D and E.
D is linked to E and linked to A. A is linked to E.
Here I highlight 2 points:
D have two links: E and A.
A node can be accessed by C and D.
This break the definition of linear structure
has a unique “predecessor” and a unique “successor”
So, you don't have a list. You have a Graph!

Return lambda Node < Node

I'm receiving this compiler error when putting a Node onto a min-priority queue.
TypeError: '<' not supported between instances of 'Node' and 'Node'
Here is where it fails
from queue import PriorityQueue
def huffman(text):
A = frequency(text) # returns a dictionary {(frequency, character),...}
q = PriorityQueue()
heap = build_min_heap(A) # builds min heap
while heap:
temp = pop_heap(heap) # format (frequency, character)
----> q.put(Node(temp[1],temp[0],None,None))
# Node(character, frequency, left child, right child)
def min_heapify(A, i, key=lambda a: a):
lefti = 2 * i + 1
righti = 2 * i + 2
heap_size = len(A)
--->if lefti < heap_size and key(A[lefti]) < key(A[i]):
/anaconda3/lib/python3.6/queue.py in put(self, item, block, timeout)
141 raise Full
142 self.not_full.wait(remaining)
--> 143 self._put(item)
144 self.unfinished_tasks += 1
145 self.not_empty.notify()
/anaconda3/lib/python3.6/queue.py in _put(self, item)
225
226 def _put(self, item):
--> 227 heappush(self.queue, item)
228
229 def _get(self):
I found a workaround by adding an "lt" function to the Node class.
def __lt__(self, other):
return self.frequency < other.frequency
However, is there another way to fix this using lambda expressions or modifying the min-priority queue in some way?
Maybe a key function perhaps? I understand what key(value) is doing but I don't know how would I interpret it as a Node. I tried something like the following, but it didn't work.
def key(item):
if instanceof(item, Node):
return item.frequency
Also to note that the heap functions also process int values and other types. This is way later in the code where I'm passing Nodes to the heap/queue.
Note: The Node is sorted by frequency.
Here is my Node class
class Node():
def __init__(self, character=None, frequency=None, left=None, right=None):
self.character = character
self.frequency = frequency
self.left = left
self.right = right
def isLeaf(self):
return self.left is None and self.right is None
def __lt__(self, other):
return self.frequency < other.frequency
def __repr__(self):
return 'Node({}, {}, {}, {})'.format(self.character,
self.frequency,
self.left, self.right)
def min_heapify(A, i, key=lambda a: a):
lefti = 2 * i + 1
righti = 2 * i + 2
heap_size = len(A)
if lefti < heap_size and key(A[lefti]) < key(A[i]):
smallesti = lefti
else:
smallesti = i
if righti < heap_size and key(A[righti]) < key(A[smallesti]):
smallesti = righti
if smallesti != i:
A[i], A[smallesti] = A[smallesti], A[i]
min_heapify(A, smallesti, key)
def build_min_heap(A, key=lambda a: a):
for i in range(len(A) // 2 - 1, -1, -1):
swaps = min_heapify(A, i, key)
return A
A final word, I understand that "lt" works in the Node class, but I'm trying to figure out another solution that doesn't involve modifying the Node class because other sites have comments that say to use a lambda expression (e.g. The key function should return the frequency stored in the argument to key, which should be a Node.; do we need to write the key function? Yes. It can be a simple lambda expression.) but they are vague in how to accomplish that.

Why are the elements in my heapq not ordered python?

I am using a simple heapq in python with custom elements on which I implemented the lt function.
class Edge:
def __init__(self, cost, u, v):
self.u = u
self.v = v
self.cost = cost
def weight(self):
w = self.cost
v = self.v
while v.parent is not None:
w += v.const
v = v.parent
return w
def __lt__(self, other):
return self.weight() < other.weight()
Then I keep a heap of these elements in another array called P:
class Vertex:
def __init__(self, node=None):
#other stuff omited #####
self.P = []
def add_incoming_nodes(self, subgraph):
for node, costs in subgraph.items():
#if costs[self.vertex] is not 0: #node is not self
#push endpoints of the edge from another vertex to this vertex
heapq.heappush(self.P, Edge(costs[self.vertex], node, self))
The problem is that when I heappop an element, I would expect it to be the smallest element in my array right ? But this assertion here fails
#select arbitrary vertex
a = all_nodes[0]
while a.P: #while P[a] is not ∅
edge = heapq.heappop(a.P)
for a_edge in a.P:
assert edge.weight() < a_edge.weight()

int' object has no attribute '__getitem__' Python

This is the tree problem
class TreeProblem(Problem):
# methods exported to Search class ---------------------------------------------
# initial "state" of a tree is just its root
def initialState(self):
return self.root.id
# goal test is whether goal matches node label
def goalTest(self, state):
node = self.nodes[state]
return node.isgoal
# successors of a node are its children
def successorFn(self, state):
node = self.nodes[state]
if node.isTerminal():
return []
else:
return [self._contents('l', node.lchild), \
self._contents('r', node.rchild)]
# step cost state--[action]-->result is just the random cost associated with result
def stepCost(self, state, action, result):
node = self.nodes[result]
return node.cost
# no action is actually taken; we just print out the state for debugging
def takeAction(self, state, action):
print 'visit ', state
return 'visit', state
This is the Node class
class Node:
"""A node in a search tree. Contains a pointer to the parent (the node
that this is a successor of) and to the actual state for this node. Note
that if a state is arrived at by two paths, then there are two nodes with
the same state. Also includes the action that got us to this state, and
the total path_cost (also known as g) to reach the node. Other functions
may add an f and h value; see best_first_graph_search and astar_search for
an explanation of how the f and h values are handled. You will not need to
subclass this class."""
def __init__(self, state, parent=None, action=None, path_cost=0):
"Create a search tree Node, derived from a parent by an action."
# state = force_hashable(state)
self.update(self, state=state, parent=parent, action=action,
path_cost=path_cost, depth=0)
if parent:
self.depth = parent.depth + 1
def __repr__(self):
return "<Node %s>" % (self.state,)
def expand(self, problem):
"List the nodes reachable in one step from this node."
return [self.child_node(problem, action)
for action in problem.takeAction(self.state, self.action)]
#print problem
#return [Node(next, self, act,
# problem.stepCost(self.state, act, next[1]))
# for (act, next) in problem.successorFn(self.state)]
def child_node(self, problem, action):
"Fig. 3.10"
(act, next) = problem.successorFn(self.state)
print str(next[1]) + "\n"
return Node(next, self, action,
problem.stepCost(self.state, action, next[1]))
def solution(self):
"Return the sequence of actions to go from the root to this node."
return [node.action for node in self.path()[1:]]
def path(self):
"Return a list of nodes forming the path from the root to this node."
node, path_back = self, []
while node:
path_back.append(node)
node = node.parent
return list(reversed(path_back))
def update(self, x, **entries):
"""Update a dict, or an object with slots, according to `entries` dict.
>>> update({'a': 1}, a=10, b=20)
{'a': 10, 'b': 20}
>>> update(Struct(a=1), a=10, b=20)
Struct(a=10, b=20)
"""
if isinstance(x, dict):
x.update(entries)
else:
x.__dict__.update(entries)
return x
# We want for a queue of nodes in breadth_first_search or
# astar_search to have no duplicated states, so we treat nodes
# with the same state as equal. [Problem: this may not be what you
# want in other contexts.]
def __eq__(self, other):
return isinstance(other, Node) and self.state == other.state
def __hash__(self):
return hash(str(self.state))
This is the dfs_search code:
def depth_limited_search(self, problem, limit=50):
return self.recursive_dls(Node(problem.initialState()), problem, limit)
def recursive_dls(self, node, problem, limit):
print str(type(node.state)) + "\n"
new_state=node.state
print new_state[1]
if problem.goalTest(node.state):
return node
elif node.depth == limit:
return 'cutoff'
else:
cutoff_occurred = False
for child in node.expand(problem):
result = self.recursive_dls(child, problem, limit)
if result == 'cutoff':
cutoff_occurred = True
elif result is not None:
return result
return if_(cutoff_occurred, 'cutoff', None)
here, node.state type is tuple
which has value ('r',2)
when i say node.state[1]
it gives int' object has no attribute 'getitem'
the above error
please guide here

Categories

Resources