def test_iterable_becomes_attribute_if_passed_as_argument(eh): """An iterable in our case is limited to list, tuple.""" from binary_heap import BinaryHeap b = BinaryHeap([1, 2, 3]) c = BinaryHeap((1, 2, 3)) assert type(b.iterable) == list assert type(c.iterable) == tuple
def test_binary_heap_push(self): bh = BinaryHeap() self.assertEqual(bh.size(), 0) bh.push(1) bh.push(2) bh.push(3) self.assertEqual(bh.size(), 3)
def test_binary_heap_max(self): bh = BinaryHeap() bh.push(1) bh.push(3) bh.push(2) self.assertEqual(bh.max(), 3) self.assertEqual(bh.size(), 3)
def shorted_path(G, src): dist = {} pred = {} n = 0 infinity = sys.maxint for v in G.keys(): dist[v] = infinity pred[v] = None n +=1 dist[src] = 0 bh = BinaryHeap(n, src, infinity) while not bh.isEmpty(): u = bh.pop() for v, w in G[u]: newLen = dist[u] + w if newLen < dist[v]: dist[v] = newLen bh.decreaseKey(v, newLen) pred[v] = u return dist, pred
def test_unique_values_raises_error_if_1_value_in_list(): """If a value is pushed into heap that already exists, error is raised.""" from binary_heap import BinaryHeap b = BinaryHeap() b.push(1) with pytest.raises(ValueError): b.push(1)
def test_heap_delete_min_build_heap(benchmark, dsa_, values): nums = deepcopy(values) dsa_obj = BinaryHeap() dsa_obj.build_heap(values) to_del = values[len(values) // 2] benchmark(dsa_obj.delete_min)
def test_pop(): bt = BinaryHeap() for i in range(50): bt.push(random.randint(0,1000)) for i in range(49): tmp = max(bt._list[1:]) bt.pop() assert tmp == bt._list[0] bt.pop() assert 0 == len(bt._list) # testing for empty list pop a = BinaryHeap() with pytest.raises(IndexError): assert a.pop()
def least_cost_path(graph, start, dest, cost): reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance((edge[1], nbr))) # if the dest is not in reached, then no route was found if dest not in reached: return [] current = dest route = [current] # go through the reached vertices until we get back to start and append # each vertice that we "stop" at while current != start: current = reached[current] route.append(current) # reverse the list because we made a list that went from the dest to start route = route[::-1] return route
def __init__(self, width, length, height): """ Generates all maze cells and edges """ self.width = width self.length = length self.height = height self.generated = False self.graph = Graph((width, length, height)) self.edge_queue = BinaryHeap() self.edges_added = 0 self.inter_layer_edges = 0 for x in range(width): for y in range(length): for z in range(height): self.graph.add_vertex((x, y, z)) self.areas = UnionFind([cell.get_pos() for cell in self]) self.edge_choices = RandomSet(self.graph.get_all_edges()) self.queue_factor = len(self.edge_choices) // LAYER_FACTOR for _ in range(self.queue_factor): edge = self.edge_choices.pop_random() self._queue_edge(edge) self.player = self.graph.get_cell((0, 0, 0)) self.end_cell = self.graph.get_cell( (self.width - 1, self.length - 1, self.height - 1)) self.end_cell.is_end = True
def test_update_with_lower_value_for_leaf_node(self): values = [1, 3, 5, 7, 9, 10, 11] names = ['A', 'B', 'C', 'D', 'E', 'F', 'G'] binary_heap = BinaryHeap(self.setup(names, values)) binary_heap.build_heap() binary_heap.update(6, 4) self.assertEqual([x.get_value_attribute() for x in binary_heap.node_list if x is not None], [1, 3, 4, 7, 9, 5, 11])
def test_delete_min(self): names = ['A', 'B', 'C', 'D', 'E', 'F', 'G'] values = [5, 3, 7, 1, 9, 10, 11] binary_heap = BinaryHeap(self.setup(names, values)) binary_heap.build_heap() self.assertEqual([x.get_value_attribute() for x in binary_heap.node_list if x is not None], [1, 3, 7, 5, 9, 10, 11]) self.assertEqual(binary_heap.delete_min().get_value_attribute(), 1)
def test_init_max(): #TEST for MAX a = BinaryHeap("max",*[random.randint(0,100) for i in range(37)]) index = a._size - 1 while index : assert a._list[index] <= a._list[(index-1)//2] index -= 1
def __init__(self, iterable=()): """Initialize a priority queue, optionally with items from iterable. The items in the priority queue are stored in a binary minheap. Items are first sorted by priority, then queue insertion order. Priority is expressed as an integer with 0 being the most important. args: iterable: an optional iterable to add to the priority queue. Items added this way will be given a priority of None. each item inside iterable can be either: * A QNode object * A container with value, priority * A non-iterable value """ self.heap = BinaryHeap(iterable=()) for item in iterable: try: is_container = len(item) == 2 except TypeError: # Case of QNode or non-iterable item self.insert(item) else: if is_container: # Case of value, iterable self.insert(item[0], item[1]) else: raise TypeError( "More than two args: instantiation supports\ non-iter value or iter of value, priority")
def least_cost_path(graph, start, dest, cost): reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance( (edge[1], nbr))) if dest not in reached: return [] current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] return path
def test_insert(self): bh = BinaryHeap(compare=lambda a, b: a < b) bh.insert(3) bh.insert(3) bh.insert(2) self.assertSequenceEqual([3, 3, 2], bh.values) bh.insert(4) self.assertSequenceEqual([4, 3, 2, 3], bh.values)
def test_correctness(self): for case in cases: bheap = BinaryHeap() bheap.build_heap(case) retrieved = [] while len(bheap) > 0: retrieved.append(bheap.delete_min()) self.assertEqual(retrieved, sorted(case))
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 least_cost_path(graph, start, dest, location): """ *** NOTE: This function is a modified version of our previous *** *** implementation of Dijkstra's Algorithm from Assignment 1 *** *** Part 1. Comments in code indicate changes made. *** Find and return a least cost path in graph from start vertex to dest vertex. Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. location: A dictionary containing all the vertices on the screen. Keys of location are identifiers holding the vertices as values. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} events = BinaryHeap() events.insert((start, start), 0) while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] # each vertex in our graph is a tuple, so we use tuples to find neighbours # instead of single values to find the neighbours of a vertex for nbr in graph.neighbours(location[edge[1]]): # find the identifier/key value attached to nbr tuple nbrID = neighbour_identifier(nbr, location) # insert to binary heap appropriately events.insert((edge[1], nbrID), time + distance(location[edge[1]], nbr)) # FIND MINIMUM PATH IF POSSIBLE if dest not in reached: return [] # start at the dest and continously ind the parent of current vertex # until have reached starting vertex current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] # reverse the list so starts from the ghost return path
def test_build_heap_extract_all_elements_should_return_sorted_elements(self): nums = [3, 4, -1, 15, 2, 77, -3, 4, 12] heap = BinaryHeap(nums) result = [] expected = [77, 15, 12, 4, 4, 3, 2, -1, -3] while len(heap) > 0: result.append(heap.extract_max()) self.assertEqual(result, expected)
def test_push(): for x in range(50): a = BinaryHeap() for i in range(47): a.push(random.randint(0,100)) assert max(a._list) == a._list[0] index = a._size - 1 while index : assert a._list[index] <= a._list[(index-1)//2] index -= 1
def test_pop_returns_sorted_values(): """Test pop entire bin returns sorted values.""" from binary_heap import BinaryHeap import random rand_nums = list(set([random.randint(0, 1000000000) for i in range(10000)])) b = BinaryHeap(rand_nums) # import pdb; pdb.set_trace() all_popped = [b.pop() for i in range(1, len(b.heap))] assert all_popped == sorted(rand_nums, reverse=True)
def test_empty_heap_insert_extract_elements_should_return_sorted_elements(self): nums = [3, 4, -1, 15, 2, 77, -3, 4, 12] heap = BinaryHeap() expected = [77, 15, 12, 4, 4, 3, 2, -1, -3] result = [] for num in nums: heap.insert(num) while heap.count > 0: result.append(heap.extract_max()) self.assertEqual(result, expected)
def least_cost_path_lrt(self, lrts, end): reached = {} events = BinaryHeap() #insert start location with time of 0 into the heap counter = 0 for key, val in lrts.items(): events.insert((val, val, 0), 0) while (len(events)) > 0: pair, time = events.popmin() #print(time) #account for heuristic so it is not compounded if (pair[2]): time -= pair[2] if pair[1] not in reached: # add to reached dictionary reached[pair[1]] = pair[0] #consider neighbors around each point and distance to each neighbor for neighbour in self.graph.neighbours(pair[1]): point1 = self.locations[pair[1]] point2 = self.locations[neighbour] # maybe we could save distances between objects in a dict, and reuse them? # add heuristic in for the form of euclidean distance. This will helps lead the searching algorithm # more towards the end location heuristic = euclidean_distance(point2, self.locations[end]) # add key and val to BinaryHeap called events events.insert( (pair[1], neighbour, heuristic), time + euclidean_distance(point1, point2) + heuristic) # end loop right after destination is popped if (pair[1] == end): break # use the get_path included with the breadth_first_search helper file to find path from reached dictionary path = None closestLRT = None for key, val in lrts.items(): if val in reached: path = get_path(reached, val, end) if path == None: pass else: closestLRT = key break return path, closestLRT
def heap_sort(arr): ''' This consists of 2 steps: 1. build a min heap, which is O(nlogn) 2. extract all n elements of the heap, which is O(nlogn) Overall, this takes O(nlogn) ''' heap = BinaryHeap(arr) result = [] while not heap.is_empty(): result.append(heap.extract_min()) return result
def merge_list_using_heap(list1, list2, len_list1, len_list2): heap_instance = BinaryHeap() heap_instance.buildHeap(list2) for i in range(len_list1): if list1[i] > heap_instance.heaplist[0]: list1[i], heap_instance.heaplist[0] = heap_instance.heaplist[ 0], list1[i] heap_instance.shiftDown(0) heap_instance.inplace_sort() sorted_list = heap_instance.getHeap() sorted_list.reverse() print(list1 + sorted_list) return
def least_cost_path(graph, start, dest, cost): """Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] # burn vertex v, record predecessor u for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance((edge[1], nbr))) # if the dest is not in the reached dictionary, then return an empty list if dest not in reached: return [] # IMPLEMENTED FROM GET_PATH FUNCTION FROM breadth_first_search # finds minimum path # start at the dest and continuosly and find the parent of current vertex # until have reached starting vertex current = dest path = [current] while current != start: current = reached[current] path.append(current) path = path[::-1] return path
def least_cost_path(graph, start, dest, cost): # Implementation of Dijkstra's algorithm events = BinaryHeap() reached = dict() events.insert((start, start), 0) # Begin at time 0, at the start vertex while events: edge, time = events.popmin() # Get next burnt vertex if edge[1] not in reached: # If the destination is not been reached reached[edge[1]] = edge[0] # Keep track of where we came from for w in graph.neighbours(edge[1]): # Burn the neighbours!!!! events.insert(((edge[1]), w), (time + cost.distance( (edge[1], w)))) # Add the fuse return get_path(reached, start, dest) # return the path
def kruskal(V, E, W): ds = DisjointSets() min_weight_priority = MinEdgePriority(W) heap = BinaryHeap(min_weight_priority) A = [] for v in V: ds.make_set(v) for e in E: heap.insert(e) while not heap.is_empty(): (u, v) = heap.extract() if ds.find_set(u) != ds.find_set(v): A.append((u, v)) ds.union(u, v) return A
def least_cost_path(graph, start, dest, cost): """Find and return a least cost path in graph from start vertex to dest vertex. Efficiency: If E is the number of edges, the run-time is O( E log(E) ). Args: graph (Graph): The digraph defining the edges between the vertices. start: The vertex where the path starts. It is assumed that start is a vertex of graph. dest: The vertex where the path ends. It is assumed that dest is a vertex of graph. cost: A class with a method called "distance" that takes as input an edge (a pair of vertices) and returns the cost of the edge. For more details, see the CostDistance class description below. Returns: list: A potentially empty list (if no path can be found) of the vertices in the graph. If there was a path, the first vertex is always start, the last is always dest in the list. Any two consecutive vertices correspond to some edge in graph. """ reached = {} # empty dictionary events = BinaryHeap() # empty heap events.insert((start, start), 0) # vertex s burns at time 0 while len(events) > 0: edge, time = events.popmin() if edge[1] not in reached: reached[edge[1]] = edge[0] for nbr in graph.neighbours(edge[1]): events.insert((edge[1], nbr), time + cost.distance( (edge[1], nbr))) # if the dest is not in reached, then no route was found if dest not in reached: return [] current = dest route = [current] # go through the reached vertices until we get back to start and append # each vertice that we "stop" at while current != start: current = reached[current] route.append(current) # reverse the list because we made a list that went from the dest to start route = route[::-1] return route
def a_star(G: Graph, start: int, stop: int): H = G.H open = BinaryHeap() for succ in G.get_succesors(start): open.add([G[start][succ] + H[succ][stop], start, succ]) closed = Map(start, len(G)) while True: f, u, x = open.get() g = f - H[x][stop] if g < closed[x][0]: closed[x] = (round(g, 2), u) for ng in G.get_succesors(x): if ng not in closed: open.add((closed[x][0] + G[x][ng] + H[ng][stop], x, ng)) if x == stop: return closed.reconstruct_path(start, stop)