diff --git a/README.md b/README.md index 5791f59e7..ca68ad5ee 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,11 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and | 3.14 | Uniform-Cost-Search | `uniform_cost_search` | [`search.py`][search] | Done | | 3.17 | Depth-Limited-Search | `depth_limited_search` | [`search.py`][search] | Done | | 3.18 | Iterative-Deepening-Search | `iterative_deepening_search` | [`search.py`][search] | Done | -| 3.22 | Best-First-Search | `best_first_graph_search` | [`search.py`][search] | | +| 3.22 | Best-First-Search | `best_first_graph_search` | [`search.py`][search] | Done | | 3.24 | A\*-Search | `astar_search` | [`search.py`][search] | Done | | 3.26 | Recursive-Best-First-Search | `recursive_best_first_search` | [`search.py`][search] | Done | -| 4.2 | Hill-Climbing | `hill_climbing` | [`search.py`][search] | | -| 4.5 | Simulated-Annealing | `simulated_annealing` | [`search.py`][search] | | +| 4.2 | Hill-Climbing | `hill_climbing` | [`search.py`][search] | Done | +| 4.5 | Simulated-Annealing | `simulated_annealing` | [`search.py`][search] | Done | | 4.8 | Genetic-Algorithm | `genetic_algorithm` | [`search.py`][search] | Done | | 4.11 | And-Or-Graph-Search | `and_or_graph_search` | [`search.py`][search] | Done | | 4.21 | Online-DFS-Agent | `online_dfs_agent` | [`search.py`][search] | | @@ -60,7 +60,7 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and | 6 | CSP | `CSP` | [`csp.py`][csp] | Done | | 6.3 | AC-3 | `AC3` | [`csp.py`][csp] | Done | | 6.5 | Backtracking-Search | `backtracking_search` | [`csp.py`][csp] | Done | -| 6.8 | Min-Conflicts | `min_conflicts` | [`csp.py`][csp] | | +| 6.8 | Min-Conflicts | `min_conflicts` | [`csp.py`][csp] | Done | | 6.11 | Tree-CSP-Solver | `tree_csp_solver` | [`csp.py`][csp] | Done | | 7 | KB | `KB` | [`logic.py`][logic] | Done | | 7.1 | KB-Agent | `KB_Agent` | [`logic.py`][logic] | Done | @@ -71,7 +71,7 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and | 7.15 | PL-FC-Entails? | `pl_fc_resolution` | [`logic.py`][logic] | Done | | 7.17 | DPLL-Satisfiable? | `dpll_satisfiable` | [`logic.py`][logic] | Done | | 7.18 | WalkSAT | `WalkSAT` | [`logic.py`][logic] | Done | -| 7.20 | Hybrid-Wumpus-Agent | `HybridWumpusAgent` | [`logic.py`][logic]\* | | +| 7.20 | Hybrid-Wumpus-Agent | `HybridWumpusAgent` | | | | 7.22 | SATPlan | `SAT_plan` | [`logic.py`][logic] | Done | | 9 | Subst | `subst` | [`logic.py`][logic] | Done | | 9.1 | Unify | `unify` | [`logic.py`][logic] | Done | @@ -102,7 +102,7 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and | 16.9 | Information-Gathering-Agent | | | | 17.4 | Value-Iteration | `value_iteration` | [`mdp.py`][mdp] | Done | | 17.7 | Policy-Iteration | `policy_iteration` | [`mdp.py`][mdp] | Done | -| 17.7 | POMDP-Value-Iteration | | | | +| 17.9 | POMDP-Value-Iteration | | | | | 18.5 | Decision-Tree-Learning | `DecisionTreeLearner` | [`learning.py`][learning] | Done | | 18.8 | Cross-Validation | `cross_validation` | [`learning.py`][learning] | | | 18.11 | Decision-List-Learning | `DecisionListLearner` | [`learning.py`][learning]\* | | @@ -110,13 +110,13 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and | 18.34 | AdaBoost | `AdaBoost` | [`learning.py`][learning] | | | 19.2 | Current-Best-Learning | `current_best_learning` | [`knowledge.py`](knowledge.py) | Done | | 19.3 | Version-Space-Learning | `version_space_learning` | [`knowledge.py`](knowledge.py) | Done | -| 19.8 | Minimal-Consistent-Det | | | +| 19.8 | Minimal-Consistent-Det | `minimal_consistent_det` | [`knowledge.py`](knowledge.py) | Done | | 19.12 | FOIL | | | | 21.2 | Passive-ADP-Agent | `PassiveADPAgent` | [`rl.py`][rl] | Done | | 21.4 | Passive-TD-Agent | `PassiveTDAgent` | [`rl.py`][rl] | Done | | 21.8 | Q-Learning-Agent | `QLearningAgent` | [`rl.py`][rl] | Done | | 22.1 | HITS | `HITS` | [`nlp.py`][nlp] | Done | -| 23 | Chart-Parse | `Chart` | [`nlp.py`][nlp] | | +| 23 | Chart-Parse | `Chart` | [`nlp.py`][nlp] | Done | | 23.5 | CYK-Parse | `CYK_parse` | [`nlp.py`][nlp] | Done | | 25.9 | Monte-Carlo-Localization| `monte_carlo_localization` | [`probability.py`][probability] | Done | diff --git a/knowledge.py b/knowledge.py index a5d165e3e..6330923bd 100644 --- a/knowledge.py +++ b/knowledge.py @@ -207,6 +207,7 @@ def build_h_combinations(hypotheses): def minimal_consistent_det(E, A): + """Returns a minimal set of attributes which give consistent determination""" n = len(A) for i in range(n + 1): @@ -216,6 +217,7 @@ def minimal_consistent_det(E, A): def consistent_det(A, E): + """Checks if the attributes(A) is consistent with the examples(E)""" H = {} for e in E: diff --git a/search.py b/search.py index 31d3f0940..68b77a5a8 100644 --- a/search.py +++ b/search.py @@ -509,6 +509,47 @@ def and_search(states, problem, path): return or_search(problem.initial, problem, []) +class PeakFindingProblem(Problem): + """Problem of finding the highest peak in a limited grid""" + + def __init__(self, initial, grid): + """The grid is a 2 dimensional array/list whose state is specified by tuple of indices""" + Problem.__init__(self, initial) + self.grid = grid + self.n = len(grid) + assert self.n > 0 + self.m = len(grid[0]) + assert self.m > 0 + + def actions(self, state): + """Allows movement in only 4 directions""" + # TODO: Add flag to allow diagonal motion + allowed_actions = [] + if state[0] > 0: + allowed_actions.append('N') + if state[0] < self.n - 1: + allowed_actions.append('S') + if state[1] > 0: + allowed_actions.append('W') + if state[1] < self.m - 1: + allowed_actions.append('E') + return allowed_actions + + def result(self, state, action): + """Moves in the direction specified by action""" + x, y = state + x = x + (1 if action == 'S' else (-1 if action == 'N' else 0)) + y = y + (1 if action == 'E' else (-1 if action == 'W' else 0)) + return (x, y) + + def value(self, state): + """Value of a state is the value it is the index to""" + x, y = state + assert 0 <= x < self.n + assert 0 <= y < self.m + return self.grid[x][y] + + class OnlineDFSAgent: """[Figure 4.21] The abstract class for an OnlineDFSAgent. Override diff --git a/tests/test_csp.py b/tests/test_csp.py index 78afac673..5a10f5ce5 100644 --- a/tests/test_csp.py +++ b/tests/test_csp.py @@ -330,6 +330,15 @@ def test_backtracking_search(): order_domain_values=lcv, inference=mac) +def test_min_conflicts(): + random.seed("aima-python") + assert min_conflicts(australia) + assert min_conflicts(usa) + assert min_conflicts(france) + australia_impossible = MapColoringCSP(list('RG'), 'SA: WA NT Q NSW V; NT: WA Q; NSW: Q V; T: ') + assert min_conflicts(australia_impossible, 1000) is None + + def test_universal_dict(): d = UniversalDict(42) assert d['life'] == 42 diff --git a/tests/test_search.py b/tests/test_search.py index af892f6f1..f22ca6f89 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -6,7 +6,6 @@ vacumm_world = GraphProblemStochastic('State_1', ['State_7', 'State_8'], vacumm_world) LRTA_problem = OnlineSearchProblem('State_3', 'State_5', one_dim_state_space) - def test_find_min_edge(): assert romania_problem.find_min_edge() == 70 @@ -20,6 +19,22 @@ def test_breadth_first_search(): assert breadth_first_search(romania_problem).solution() == ['Sibiu', 'Fagaras', 'Bucharest'] +def test_best_first_graph_search(): + # uniform_cost_search and astar_search test it indirectly + assert best_first_graph_search( + romania_problem, + lambda node: node.state).solution() == ['Sibiu', 'Fagaras', 'Bucharest'] + assert best_first_graph_search( + romania_problem, + lambda node: node.state[::-1]).solution() == ['Timisoara', + 'Lugoj', + 'Mehadia', + 'Drobeta', + 'Craiova', + 'Pitesti', + 'Bucharest'] + + def test_uniform_cost_search(): assert uniform_cost_search( romania_problem).solution() == ['Sibiu', 'Rimnicu', 'Pitesti', 'Bucharest'] @@ -56,6 +71,33 @@ def test_recursive_best_first_search(): romania_problem).solution() == ['Sibiu', 'Rimnicu', 'Pitesti', 'Bucharest'] +def test_hill_climbing(): + prob = PeakFindingProblem((0, 0), [[0, 5, 10, 20], + [-3, 7, 11, 5]]) + assert hill_climbing(prob) == (0, 3) + prob = PeakFindingProblem((0, 0), [[0, 5, 10, 8], + [-3, 7, 9, 999], + [1, 2, 5, 11]]) + assert hill_climbing(prob) == (0, 2) + prob = PeakFindingProblem((2, 0), [[0, 5, 10, 8], + [-3, 7, 9, 999], + [1, 2, 5, 11]]) + assert hill_climbing(prob) == (1, 3) + + +def test_simulated_annealing(): + random.seed("aima-python") + prob = PeakFindingProblem((0, 0), [[0, 5, 10, 20], + [-3, 7, 11, 5]]) + sols = {prob.value(simulated_annealing(prob)) for i in range(100)} + assert max(sols) == 20 + prob = PeakFindingProblem((0, 0), [[0, 5, 10, 8], + [-3, 7, 9, 999], + [1, 2, 5, 11]]) + sols = {prob.value(simulated_annealing(prob)) for i in range(100)} + assert max(sols) == 999 + + def test_BoggleFinder(): board = list('SARTELNID') """