def test_priority_queue(): """ Ensure the priority set is sorting elements correctly. """ random_elements = [i for i in range(10)] shuffle(random_elements) pq = PriorityQueue() for e in random_elements: pq.push(e) random_elements.sort() assert [e for e in pq] == random_elements assert pq.peek() == random_elements[0] output = [] while len(pq) > 0: output.append(pq.pop()) assert output == random_elements for e in random_elements: pq.push(e) assert len(pq) == 10 pq.update_cost_limit(5) assert len(pq) == 6 output = [] while len(pq) > 0: output.append(pq.pop()) assert output == random_elements[:6] pq = PriorityQueue(node_value=lambda x: x, max_length=3) pq.push(6) pq.push(0) pq.push(2) pq.push(6) pq.push(7) assert len(pq) == 3 assert list(pq) == [0, 2, 6] pq.update_cost_limit(5) assert len(pq) == 2 assert pq.peek() == 0 assert pq.peek_value() == 0 assert pq.pop() == 0 assert pq.peek() == 2 assert pq.peek_value() == 2 assert pq.pop() == 2 assert len(pq) == 0
def branch_and_bound(problem, graph=True, depth_limit=float('inf')): """ An exhaustive optimization technique that is guranteed to give the best solution. In general the algorithm starts with some (potentially non-optimal) solution. Then it uses the cost of the current best solution to prune branches of the search that do not have any chance of being better than this solution (i.e., that have a node_value > current best cost). In this implementation, node_value should provide an admissible lower bound on the cost of solutions reachable from the provided node. If node_value is inadmissible, then optimality guarantees are lost. Also, if the search space is infinite and/or the node_value function provides too little guidance (e.g., node_value = float('-inf')), then the search might never terminate. To counter this, a depth_limit can be provided that stops expanding nodes after the provided depth. This will ensure the search is finite and guaranteed to terminate. Finally, the problem.goal_test function can be used to terminate search early if a good enough solution has been found. If goal_test(node) return True, then search is immediately terminated and the node is returned. Note, the current implementation uses best-first search via a priority queue data structure. :param problem: The problem to solve. :type problem: :class:`py_search.base.Problem` :param graph: Whether to use graph search (no duplicates) or tree search (duplicates). :type graph: Boolean """ b = None bv = float('inf') fringe = PriorityQueue(node_value=problem.node_value) fringe.push(problem.initial) if graph: closed = set() closed.add(problem.initial) while len(fringe) > 0: pv = fringe.peek_value() if bv < pv: break node = fringe.pop() if problem.goal_test(node, problem.goal): yield SolutionNode(node, problem.goal) if problem.node_value(node) < bv: b = node bv = problem.node_value(node) fringe.update_cost_limit(bv) if depth_limit == float('inf') or node.depth() < depth_limit: for s in problem.successors(node): if not graph: fringe.push(s) elif s not in closed: fringe.push(s) closed.add(s) yield SolutionNode(b, problem.goal)