def test_depth_first_graph_search(): """ depth-first graph and tree search are the same on this problem. """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(depth_first_search(p, graph=True)) assert sol.state_node.state == goal assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1 p = AnnotatedProblem(EasyProblem(0, goal)) sol = next( depth_first_search(p, graph=True, forward=False, backward=True)) assert sol.state_node == sol.goal_node assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1 p2 = AnnotatedProblem(EasyProblem2(0, goal)) try: next(depth_first_search(p2, graph=True)) assert False except StopIteration: assert p2.nodes_expanded == 2 assert p2.goal_tests == 1
def test_branch_and_bound(): initial = 0 goal = 10 limits = (-goal, goal) p = AnnotatedProblem( EasyProblem(initial, initial_cost=initial, extra=limits)) sol = next(branch_and_bound(p)) assert abs(sol.state_node.state - limits[0]) <= 0.1 sol = next(branch_and_bound(p, graph=False)) assert abs(sol.state_node.state - limits[0]) <= 0.1 p2 = HillProblem(initial, goal=goal, initial_cost=initial, extra=limits) sol = next(branch_and_bound(p2, graph=False)) assert abs(sol.state_node.state - goal) <= 0.1 p3 = AnnotatedProblem( HillProblem(initial, goal=initial, initial_cost=initial, extra=limits)) sol = next(branch_and_bound(p3)) assert p3.nodes_expanded == 0 assert p3.goal_tests == 1 assert abs(sol.state_node.state - initial) <= 0.1 p4 = PlateauProblem(0) sol = list(branch_and_bound(p4)) assert len(sol) == 1 assert p4.node_value(sol[0].state_node) == -6 p5 = PlateauProblem(0) sol = list(branch_and_bound(p5, depth_limit=1)) assert len(sol) == 1 assert p5.node_value(sol[0].state_node) == -5
def test_local_beam_search(): initial = 0 goal = 10 limits = (-goal, goal) p = AnnotatedProblem( EasyProblem(initial, initial_cost=initial, extra=limits)) sol = next(local_beam_search(p, graph=False)) assert abs(sol.state_node.state - limits[0]) <= 0.1 p2 = HillProblem(initial, goal=goal, initial_cost=initial, extra=limits) sol = next(local_beam_search(p2)) assert abs(sol.state_node.state - goal) <= 0.1 p3 = HillProblemNoGoalTest(initial, goal=goal, initial_cost=initial, extra=limits) sol = next(local_beam_search(p3)) assert abs(sol.state_node.state - goal) <= 0.1 p4 = AnnotatedProblem( HillProblem(initial, goal=initial, initial_cost=initial, extra=limits)) sol = next(local_beam_search(p4)) assert p4.nodes_expanded == 0 assert p4.goal_tests == 1 assert abs(sol.state_node.state - initial) <= 0.1 p5 = AnnotatedProblem(PlateauProblem(initial)) sol = next(local_beam_search(p5, beam_width=2))
def test_hill_climbing(): initial = 0 goal = 10 limits = (-goal, goal) p = AnnotatedProblem( EasyProblem(initial, initial_cost=initial, extra=limits)) sol = next(hill_climbing(p, graph=False)) assert abs(sol.state_node.state - limits[0]) <= 0.1 p2 = HillProblem(initial, goal=goal, initial_cost=initial, extra=limits) sol = next(hill_climbing(p2)) assert abs(sol.state_node.state - goal) <= 0.1 p3 = AnnotatedProblem( HillProblem(initial, goal=initial, initial_cost=initial, extra=limits)) sol = next(hill_climbing(p3)) assert p3.nodes_expanded == 0 assert p3.goal_tests == 1 assert abs(sol.state_node.state - initial) <= 0.1 p4 = AnnotatedProblem(PlateauProblem(initial)) sol = next(hill_climbing(p4, random_restarts=3)) p5 = AnnotatedProblem(PlateauProblemWithGoal(initial)) sols = list(hill_climbing(p5, random_restarts=3))
def test_simulated_annealing(): initial = 0 goal = 10 limits = (-goal, goal) p = AnnotatedProblem( EasyProblem(initial, initial_cost=initial, extra=limits)) sol = next(simulated_annealing(p, temp_length=10, initial_temp=5)) assert abs(sol.state_node.state - limits[0]) <= 0.1 p2 = HillProblem(initial, goal=goal, initial_cost=initial, extra=limits) sol = next(simulated_annealing(p2)) assert abs(sol.state_node.state - goal) <= 0.1 p3 = AnnotatedProblem( HillProblem(initial, goal=initial, initial_cost=initial, extra=limits)) sol = next(simulated_annealing(p3)) assert p3.nodes_expanded == 0 assert p3.goal_tests == 1 assert abs(sol.state_node.state - initial) <= 0.1 p4 = AnnotatedProblem( HillProblem(initial, goal=goal, initial_cost=initial, extra=limits)) # sol = next(simulated_annealing(p4, limit=2)) sol = next(simulated_annealing(p4, temp_length=1, limit=2)) assert p4.nodes_expanded == 2 p5 = AnnotatedProblem( HillProblem(initial, goal=1000, initial_cost=initial, extra=limits)) # sol = next(simulated_annealing(p4, limit=2)) sol = next(simulated_annealing(p5, temp_length=100, limit=900)) assert p5.nodes_expanded == 900
def test_widening_beam_tree_search(): """ like test_beam2_tree_search, but eliminates duplicate nodes """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(widening_beam_search(p, graph=True)) assert sol.state_node.state == goal assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1 assert p.goal_tests == goal + 1 for goal in range(1, 10): p = AnnotatedProblem(HeuristicHardProblem(0, goal)) sol = next(widening_beam_search(p, graph=True)) assert sol.state_node.state == goal
def test_breadth_first_tree_search(): """ Test breadth first tree search (i.e., with duplicates). """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(breadth_first_search(p, graph=False)) assert sol.state_node.state == goal assert p.nodes_expanded == pow(2, goal + 1) - 2 assert p.goal_tests == pow(2, goal) p = AnnotatedProblem(EasyProblem(0, goal)) sol = next( breadth_first_search(p, graph=False, forward=False, backward=True)) assert sol.state_node == sol.goal_node assert p.nodes_expanded == pow(2, goal + 1) - 2 assert p.goal_tests == pow(2, goal)
def test_breadth_first_graph_search(): """ Test breadth first graph search (i.e., no duplicates). For this test problem it performs similar to breadth first. """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(breadth_first_search(p, graph=True)) assert sol.state_node.state == goal assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1 p = AnnotatedProblem(EasyProblem(0, goal)) sol = next( breadth_first_search(p, graph=True, forward=False, backward=True)) assert sol.state_node == sol.goal_node assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1
def test_beam2_graph_search(): """ like test_beam2_tree_search, but eliminates duplicate nodes """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(beam_search(p, beam_width=2, graph=True)) assert sol.state_node.state == goal assert p.nodes_expanded == goal * 2
def test_iterative_deepening_graph_search(): """ Test iterative deepening graph search. """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(iterative_deepening_search(p, graph=True)) assert sol.state_node.state == goal assert p.nodes_expanded == sum([i * 2 for i in range(1, goal + 1)]) assert p.goal_tests == sum([i + 1 for i in range(1, goal + 1)]) + 1
def test_best_first_search_with_heuristic(): """ Best heuristic is essentially depth first. """ for goal in range(1, 10): p = AnnotatedProblem(HeuristicEasyProblem(0, goal)) sol = next(best_first_search(p, graph=False)) assert sol.state_node.state == goal assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1
def test_best_first_search_without_heuristic(): """ Best first search without a heuristic is essentially breadth first. """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(best_first_search(p, graph=False)) assert sol.state_node.state == goal assert p.nodes_expanded == pow(2, goal + 1) - 2 assert p.goal_tests == pow(2, goal)
def test_beam1_tree_search(): """ Beam search with a width of 1 is like a depth first search, but with no backtracking. """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(beam_search(p, beam_width=1, graph=False)) assert sol.state_node.state == goal assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1
def test_bidirectional_tree_search(): for goal in range(1, 14): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next( breadth_first_search(p, graph=False, forward=True, backward=True)) assert sol.state_node == sol.goal_node if goal % 2 == 0: assert p.nodes_expanded == 2 * (pow(2, goal / 2 + 1) - 2) assert p.goal_tests == pow(2, goal) else: assert p.nodes_expanded == pow(2, goal // 2 + 2) - 2
def test_iterative_deepening_best_first_search(): """ Without heuristic it is basically the same as iterative_deepening_search. (here is the case where we use graph search). """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(iterative_deepening_best_first_search(p, graph=True)) assert sol.state_node.state == goal assert p.nodes_expanded == sum([i * 2 for i in range(1, goal + 2)]) - 2 assert p.goal_tests == sum([i + 1 for i in range(1, goal + 1)]) + 1
def test_beam2_tree_search(): """ Beam search with a width of 2 is slightly different. It is like a fusion of breadth and depth first searches. """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(beam_search(p, beam_width=2, graph=False)) assert sol.state_node.state == goal assert p.nodes_expanded == 2 + (goal - 1) * 4 assert p.goal_tests == 2 + (goal - 1) * 2
def test_iterative_best_first_tree_search(): """ Test iterative deepening tree search. When there is a perfect heuristic the cost is increased until the heuristic is included, then search proceeds directly to the solution. """ for goal in range(1, 10): p = AnnotatedProblem(HeuristicEasyProblem(0, goal)) sol = next(iterative_deepening_best_first_search(p, graph=False)) assert sol.state_node.state == goal assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1
def test_simulated_annealing(): initial = 0 goal = 10 limits = (-goal, goal) p = AnnotatedProblem( EasyProblem(initial, initial_cost=initial, extra=limits)) sol = next(simulated_annealing(p)) assert abs(sol.state_node.state - limits[0]) <= 0.1 p2 = HillProblem(initial, goal=goal, initial_cost=initial, extra=limits) sol = next(simulated_annealing(p2)) assert abs(sol.state_node.state - goal) <= 0.1 p3 = AnnotatedProblem( HillProblem(initial, goal=initial, initial_cost=initial, extra=limits)) sol = next(simulated_annealing(p3)) assert p3.nodes_expanded == 0 assert p3.goal_tests == 1 assert abs(sol.state_node.state - initial) <= 0.1 p4 = HillProblem(initial, goal=goal, initial_cost=initial, extra=limits) sol = next(simulated_annealing(p4, limit=2))
def test_iterative_deepening_tree_search(): """ Test iterative deepening tree search. """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(iterative_deepening_search(p, graph=False)) assert sol.state_node.state == goal assert (p.nodes_expanded == sum( [pow(2, i + 1) - 2 for i in range(1, goal)]) + (goal * 2)) assert ( p.goal_tests == sum([pow(2, i + 1) - 2 for i in range(1, goal)]) + (goal * 2) + 1)
def test_branch_and_bound(): initial = 0 goal = 10 limits = (-goal, goal) p = AnnotatedProblem( EasyProblem(initial, initial_cost=initial, extra=limits)) sol = next(branch_and_bound(p)) assert abs(sol.state_node.state - limits[0]) <= 0.1 sol = next(branch_and_bound(p, graph=False)) assert abs(sol.state_node.state - limits[0]) <= 0.1 p2 = HillProblem(initial, goal=goal, initial_cost=initial, extra=limits) sol = next(branch_and_bound(p2, graph=False)) assert abs(sol.state_node.state - goal) <= 0.1 p3 = AnnotatedProblem( HillProblem(initial, goal=initial, initial_cost=initial, extra=limits)) sol = next(branch_and_bound(p3)) assert p3.nodes_expanded == 0 assert p3.goal_tests == 1 assert abs(sol.state_node.state - initial) <= 0.1
def test_problem(): """ Tests to make sure exceptions are raised. """ p = Problem(None) try: p.predecessors(p.initial) assert False except NotImplementedError: pass try: p.successors(p.initial) assert False except NotImplementedError: pass try: p.random_node() assert False except NotImplementedError: pass # test to make sure the goal test falls back on the goal specified during # problem construction. In this case None == None - CM assert p.goal_test(p.initial) ap = AnnotatedProblem(p) # Should fall back on problem, which will raise an exception. try: ap.random_node() assert False except NotImplementedError: pass
def test_depth_first_tree_search(): """ Test depth first tree search (i.e., with duplicates). """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(depth_first_search(p, graph=False)) assert sol.state_node.state == goal assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1 p = AnnotatedProblem(EasyProblem(0, goal)) sol = next( depth_first_search(p, graph=False, forward=False, backward=True)) assert sol.state_node == sol.goal_node assert p.nodes_expanded == goal * 2 assert p.goal_tests == goal + 1 try: p = EasyProblem(0, 10) next(depth_first_search(p, graph=False, depth_limit=5)) assert False except StopIteration: pass try: p = EasyProblem(0, 10) next( depth_first_search(p, graph=False, forward=False, backward=True, depth_limit=5)) assert False except StopIteration: pass
def test_iterative_deepening_graph_search(): """ Test iterative deepening graph search. """ for goal in range(1, 10): p = AnnotatedProblem(EasyProblem(0, goal)) sol = next(iterative_deepening_search(p, graph=True)) assert sol.state_node.state == goal assert p.nodes_expanded == sum([i * 2 for i in range(1, goal + 1)]) assert p.goal_tests == sum([i + 1 for i in range(1, goal + 1)]) + 1 p = EasyProblem(0, 10) try: next(iterative_deepening_search(p, graph=True, max_depth_limit=5)) assert False except StopIteration: pass
def compare_searches(problems, searches): """ A function for comparing different search algorithms on different problems. :param problems: problems to solve. :type problems: an iterator of problems (usually a list) :param searches: search algorithms to use. :type searches: an iterator of search functions (usually a list) """ table = [] for problem in problems: for search in searches: annotated_problem = AnnotatedProblem(problem) start_time = timeit.default_timer() try: sol = next(search(annotated_problem)) elapsed = timeit.default_timer() - start_time cost = sol.cost() except StopIteration: elapsed = timeit.default_timer() - start_time cost = 'Failed' table.append([ problem.__class__.__name__, search.__name__, annotated_problem.goal_tests, annotated_problem.nodes_expanded, annotated_problem.nodes_evaluated, "%0.3f" % cost if isinstance(cost, float) else cost, "%0.4f" % elapsed if isinstance(elapsed, float) else elapsed ]) print( tabulate(table, headers=[ 'Problem', 'Search Alg', 'Goal Tests', 'Nodes Expanded', 'Nodes Evaluated', 'Solution Cost', 'Runtime' ], tablefmt="simple"))