def _setup_heap(self, heap): """before _find_min_augument_path start, this function sets up heap as the source nodes should be treated as single node. Basically, all the nodes but source nodes will be inserted into heap with appropriate weights and prev_nodes. """ min_weight_right_nodes = [sys.maxint] * len(self.table_weight) prev_of_right_nodes = [None] * len(self.table_weight) num_left_exposed_node = 0 # insert nodes in left category for i in range(0, len(self.map_match_left_to_right)): if self.map_match_left_to_right[i] == None: num_left_exposed_node += 1 left_exposed_node = HeapNode(self._global_node_id(i, True), 0) for j in range(0, len(self.table_weight)): if min_weight_right_nodes[j] > self.table_weight[i][j]: min_weight_right_nodes[j] = self.table_weight[i][j] prev_of_right_nodes[j] = left_exposed_node else: heappush(heap, HeapNode(self._global_node_id(i, True), sys.maxint)) if num_left_exposed_node == 0: heap = [] return for j in range(0, len(self.table_weight)): heappush( heap, HeapNode(self._global_node_id(j, False), min_weight_right_nodes[j], prev_of_right_nodes[j]))
def test_insert_nodes_with_descending_values(self): binary_heap = BinaryHeap() binary_heap.insert(HeapNode({'name': 'A', 'value': 0.91}, 'value')) self.assertEqual([x.get_value_attribute() for x in binary_heap.node_list if x is not None], [0.91]) binary_heap.insert(HeapNode({'name': 'B', 'value': 0.57}, 'value')) self.assertEqual([x.get_value_attribute() for x in binary_heap.node_list if x is not None], [0.57, 0.91]) binary_heap.insert(HeapNode({'name': 'B', 'value': 0.34}, 'value')) self.assertEqual([x.get_value_attribute() for x in binary_heap.node_list if x is not None], [0.34, 0.91, 0.57])
def merge_nodes(self): # if the tree has more than one node while (len(self.heap) > 1): node_1 = heapq.heappop(self.heap) node_2 = heapq.heappop(self.heap) # merge to one upper node merged = HeapNode(None, node_1.freq + node_2.freq) merged.left = node_1 merged.right = node_2 heapq.heappush(self.heap, merged)
def search(self, start_node_id, dest_node_id): # Initialize the cost structure of first intersection self.cost[start_node_id] = { 'g': 0, # True cost of getting from start node to this node. This is calculated in terms of number of hops (edges) 'h': 0, # Estimated cost of getting this node to destination node 'f': 0 # Total cost. It is a computed field f = g + h } # Put the start node as the first open node where the search starts self.open.add(start_node_id) self.cost_heap.insert(HeapNode({'id': start_node_id, 'f': 0, 'g': 0, 'h' : 0}, 'f')) iteration = 1 while len(self.open) > 0: # Among all nodes which are open find the node with minimum total cost # This will be the nest node to explore current_node = self._get_node_with_min_cost() if current_node.id == dest_node_id: end_node = current_node.id while end_node is not None: self.nodes_in_path.append(end_node) end_node = self.parent.get(end_node, None) self.nodes_in_path.reverse() return self.nodes_in_path children = self._get_next_nodes(current_node.id) for child in children: # If the intersection is already in closed set then there is no need to explore it if child in self.closed: continue g = self.cost[current_node.id]['g'] + \ math.sqrt( math.pow(self.map.intersections[current_node.id][0] - self.map.intersections[child][0], 2) + \ math.pow(self.map.intersections[current_node.id][1] - self.map.intersections[child][1], 2)) # Estimate distance to destination intersection. This is pre euclidean distance. # This heuristic will always be less than or equal to the true distance of the road # connecting the two intersections h = math.sqrt( math.pow(self.map.intersections[child][0] - self.map.intersections[dest_node_id][0], 2) + \ math.pow(self.map.intersections[child][1] - self.map.intersections[dest_node_id][1], 2)) # If the child intersection is not in open set then put it in the list to explore # Compute the cost (cost to reach this intersection, estimated cost to reach destination # from here and total cost for this intersection # The child intersection may already be there if there way another intersection leading to this one # Update the cost values if cost of reaching this child intersection is lower than cost value # associated with the intersection if child not in self.open: self.cost[child] = {'g' : g, 'h' : h, 'f' : g + h} self.parent[child] = current_node.id self.open.add(child) self.cost_heap.insert(HeapNode({'id': child, 'f': g + h, 'g': g, 'h' : h}, 'f')) elif self.cost[child]['f'] > g + h: self.cost[child] = {'g' : g, 'h' : h, 'f' : g + h} self.parent[child] = current_node.id self.cost_heap.build_heap() #if iteration >= 1: # sys.exit(0) iteration += 1
def _get_neighbors_aug_path(self, global_id_node, heap): """return neighbors in alternate path.""" assert global_id_node >= 0 and \ global_id_node < ( len(self.table_weight) * 2 ), \ "unexpected global_id: %d" % global_id if self._is_node_left_category(global_id_node): # left category if self.map_match_left_to_right[ global_id_node] == None: # exposed node return self.list_all_node_ids_right_category else: # node with match list = [] for global_id_right_node in self.list_all_node_ids_right_category: if global_id_right_node != \ self.map_match_left_to_right[global_id_node] and \ heap.count( HeapNode(global_id_right_node, 0, None, False) ) > 0: list.append(global_id_right_node) return list else: # right category # return the node in left category which can be reached through # matched edge idx = self._local_node_id(global_id_node) assert (self.map_match_right_to_left[idx] != None), \ "there is no neighbor for node %d" % global_id_node return [self.map_match_right_to_left[idx]]
def trees_merge(self): # We pick the 2 trees with the smallest frequency # Combine them into a new tree with a new parent node with the frequency of both of its children # Until you have no more trees to merge while (len(self.heap) > 1): node1 = heapq.heappop( self.heap ) #Pop and return the smallest item from the heap, maintaining the heap invariant node2 = heapq.heappop(self.heap) tree_merged = HeapNode(None, node1.frequency + node2.frequency) tree_merged.left = node1 tree_merged.right = node2 heapq.heappush(self.heap, tree_merged)
def heap_creation(self, frequency): #We use our class HeapNode to create the binary tree for key in frequency: node = HeapNode(key, frequency[key]) heapq.heappush( self.heap, node ) #Push the value of the node onto the heap, maintaining the heap invariant
def _find_min_augument_path(self): """return augument path with minimum weight given match. dijkistra's shortest path is used to find the path. """ path, heap = [], [] self._setup_heap(heap) while heap: node_entry = heappop(heap) if self._is_node_exposed_right_category(node_entry.id): tmp_node_entry = node_entry while True: path.append(tmp_node_entry.id) tmp_node_entry = tmp_node_entry.prev_node if (tmp_node_entry == None): break return path list_neighbor_node_id = \ self._get_neighbors_aug_path(node_entry.id, heap) local_id_entry_node = self._local_node_id(node_entry.id) for neighbor_node_id in list_neighbor_node_id: local_id_neighbor_node = self._local_node_id(neighbor_node_id) if self._is_node_left_category(node_entry.id): alt = node_entry.priority + \ self.table_weight[local_id_entry_node][local_id_neighbor_node] else: alt = node_entry.priority - \ self.table_weight[local_id_neighbor_node][local_id_entry_node] node_neighbor = HeapNode.get_entry(neighbor_node_id) if alt < node_neighbor.priority: node_neighbor.priority = alt node_neighbor.prev_node = node_entry heapify(heap) # end of while heap assert len(path) == 0, "path with element(s) not expected." return path
def priority_queue(self, frequency): # make a prio queue based on the fequency for key in frequency: node = HeapNode(key, frequency[key]) heapq.heappush(self.heap, node)
def setup(self, names, values): return [HeapNode({'name': n[0], 'value': n[1]}, 'value') for n in zip(names, values)]