diff --git a/README.md b/README.md
index 8bac287b6..bec48d417 100644
--- a/README.md
+++ b/README.md
@@ -64,14 +64,14 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and
| 2.7 | Table-Driven-Agent | `TableDrivenAgent` | [`agents.py`][agents] | Done | Included |
| 2.8 | Reflex-Vacuum-Agent | `ReflexVacuumAgent` | [`agents.py`][agents] | Done | Included |
| 2.10 | Simple-Reflex-Agent | `SimpleReflexAgent` | [`agents.py`][agents] | Done | Included |
-| 2.12 | Model-Based-Reflex-Agent | `ReflexAgentWithState` | [`agents.py`][agents] | | Included |
+| 2.12 | Model-Based-Reflex-Agent | `ReflexAgentWithState` | [`agents.py`][agents] | Done | Included |
| 3 | Problem | `Problem` | [`search.py`][search] | Done | Included |
| 3 | Node | `Node` | [`search.py`][search] | Done | Included |
| 3 | Queue | `Queue` | [`utils.py`][utils] | Done | No Need |
| 3.1 | Simple-Problem-Solving-Agent | `SimpleProblemSolvingAgent` | [`search.py`][search] | Done | Included |
| 3.2 | Romania | `romania` | [`search.py`][search] | Done | Included |
-| 3.7 | Tree-Search | `tree_search` | [`search.py`][search] | Done | |
-| 3.7 | Graph-Search | `graph_search` | [`search.py`][search] | Done | |
+| 3.7 | Tree-Search | `depth/breadth_first_tree_search` | [`search.py`][search] | Done | Included |
+| 3.7 | Graph-Search | `depth/breadth_first_graph_search` | [`search.py`][search] | Done | Included |
| 3.11 | Breadth-First-Search | `breadth_first_graph_search` | [`search.py`][search] | Done | Included |
| 3.14 | Uniform-Cost-Search | `uniform_cost_search` | [`search.py`][search] | Done | Included |
| 3.17 | Depth-Limited-Search | `depth_limited_search` | [`search.py`][search] | Done | Included |
@@ -93,7 +93,7 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and
| 6.8 | Min-Conflicts | `min_conflicts` | [`csp.py`][csp] | Done | Included |
| 6.11 | Tree-CSP-Solver | `tree_csp_solver` | [`csp.py`][csp] | Done | Included |
| 7 | KB | `KB` | [`logic.py`][logic] | Done | Included |
-| 7.1 | KB-Agent | `KB_AgentProgram` | [`logic.py`][logic] | Done | |
+| 7.1 | KB-Agent | `KB_AgentProgram` | [`logic.py`][logic] | Done | Included |
| 7.7 | Propositional Logic Sentence | `Expr` | [`utils.py`][utils] | Done | Included |
| 7.10 | TT-Entails | `tt_entails` | [`logic.py`][logic] | Done | Included |
| 7.12 | PL-Resolution | `pl_resolution` | [`logic.py`][logic] | Done | Included |
@@ -103,7 +103,7 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and
| 7.18 | WalkSAT | `WalkSAT` | [`logic.py`][logic] | Done | Included |
| 7.20 | Hybrid-Wumpus-Agent | `HybridWumpusAgent` | | | |
| 7.22 | SATPlan | `SAT_plan` | [`logic.py`][logic] | Done | Included |
-| 9 | Subst | `subst` | [`logic.py`][logic] | Done | |
+| 9 | Subst | `subst` | [`logic.py`][logic] | Done | Included |
| 9.1 | Unify | `unify` | [`logic.py`][logic] | Done | Included |
| 9.3 | FOL-FC-Ask | `fol_fc_ask` | [`logic.py`][logic] | Done | Included |
| 9.6 | FOL-BC-Ask | `fol_bc_ask` | [`logic.py`][logic] | Done | Included |
diff --git a/agents.ipynb b/agents.ipynb
index 65878bbab..023de8021 100644
--- a/agents.ipynb
+++ b/agents.ipynb
@@ -1252,7 +1252,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.6.4"
+ "version": "3.6.1"
}
},
"nbformat": 4,
diff --git a/logic.ipynb b/logic.ipynb
index 3097b7609..f93e0e4c5 100644
--- a/logic.ipynb
+++ b/logic.ipynb
@@ -6,18 +6,16 @@
"collapsed": true
},
"source": [
- "# Logic: `logic.py`; Chapters 6-8"
+ "# Logic"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "This notebook describes the [logic.py](https://github.com/aimacode/aima-python/blob/master/logic.py) module, which covers Chapters 6 (Logical Agents), 7 (First-Order Logic) and 8 (Inference in First-Order Logic) of *[Artificial Intelligence: A Modern Approach](http://aima.cs.berkeley.edu)*. See the [intro notebook](https://github.com/aimacode/aima-python/blob/master/intro.ipynb) for instructions.\n",
+ "This Jupyter notebook acts as supporting material for topics covered in __Chapter 6 Logical Agents__, __Chapter 7 First-Order Logic__ and __Chapter 8 Inference in First-Order Logic__ of the book *[Artificial Intelligence: A Modern Approach](http://aima.cs.berkeley.edu)*. We make use the implementations in the [logic.py](https://github.com/aimacode/aima-python/blob/master/logic.py) module. See the [intro notebook](https://github.com/aimacode/aima-python/blob/master/intro.ipynb) for instructions.\n",
"\n",
- "We'll start by looking at `Expr`, the data type for logical sentences, and the convenience function `expr`. We'll be covering two types of knowledge bases, `PropKB` - Propositional logic knowledge base and `FolKB` - First order logic knowledge base. We will construct a propositional knowledge base of a specific situation in the Wumpus World. We will next go through the `tt_entails` function and experiment with it a bit. The `pl_resolution` and `pl_fc_entails` functions will come next. We'll study forward chaining and backward chaining algorithms for `FolKB` and use them on `crime_kb` knowledge base.\n",
- "\n",
- "But the first step is to load the code:"
+ "Let's first import everything from the `logic` module."
]
},
{
@@ -31,6 +29,29 @@
"from notebook import psource"
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## CONTENTS\n",
+ "- Logical sentences\n",
+ " - Expr\n",
+ " - PropKB\n",
+ " - Knowledge-based agents\n",
+ " - Inference in propositional knowledge base\n",
+ " - Truth table enumeration\n",
+ " - Proof by resolution\n",
+ " - Forward and backward chaining\n",
+ " - DPLL\n",
+ " - WalkSAT\n",
+ " - SATPlan\n",
+ " - FolKB\n",
+ " - Inference in first order knowledge base\n",
+ " - Unification\n",
+ " - Forward chaining algorithm\n",
+ " - Backward chaining algorithm"
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {
@@ -527,6 +548,170 @@
"$B_{2, 1} \\iff (P_{1, 1} \\lor P_{2, 2} \\lor P_{3, 2})$ is converted in similar manner."
]
},
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Knowledge based agents"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "A knowledge-based agent is a simple generic agent that maintains and handles a knowledge base.\n",
+ "The knowledge base may initially contain some background knowledge.\n",
+ "
\n",
+ "The purpose of a KB agent is to provide a level of abstraction over knowledge-base manipulation and is to be used as a base class for agents that work on a knowledge base.\n",
+ "
\n",
+ "Given a percept, the KB agent adds the percept to its knowledge base, asks the knowledge base for the best action, and tells the knowledge base that it has infact taken that action.\n",
+ "
\n",
+ "Our implementation of `KB-Agent` is encapsulated in a class `KB_AgentProgram` which inherits from the `KB` class.\n",
+ "
\n",
+ "Let's have a look."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ "
\n",
+ " Codestin Search App\n",
+ " \n",
+ " \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "def KB_AgentProgram(KB):\n",
+ " """A generic logical knowledge-based agent program. [Figure 7.1]"""\n",
+ " steps = itertools.count()\n",
+ "\n",
+ " def program(percept):\n",
+ " t = next(steps)\n",
+ " KB.tell(make_percept_sentence(percept, t))\n",
+ " action = KB.ask(make_action_query(t))\n",
+ " KB.tell(make_action_sentence(action, t))\n",
+ " return action\n",
+ "\n",
+ " def make_percept_sentence(percept, t):\n",
+ " return Expr("Percept")(percept, t)\n",
+ "\n",
+ " def make_action_query(t):\n",
+ " return expr("ShouldDo(action, {})".format(t))\n",
+ "\n",
+ " def make_action_sentence(action, t):\n",
+ " return Expr("Did")(action[expr('action')], t)\n",
+ "\n",
+ " return program\n",
+ "
\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "psource(KB_AgentProgram)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The helper functions `make_percept_sentence`, `make_action_query` and `make_action_sentence` are all aptly named and as expected,\n",
+ "`make_percept_sentence` makes first-order logic sentences about percepts we want our agent to receive,\n",
+ "`make_action_query` asks the underlying `KB` about the action that should be taken and\n",
+ "`make_action_sentence` tells the underlying `KB` about the action it has just taken."
+ ]
+ },
{
"cell_type": "markdown",
"metadata": {},
@@ -539,7 +724,7 @@
},
{
"cell_type": "code",
- "execution_count": 21,
+ "execution_count": 22,
"metadata": {},
"outputs": [
{
@@ -691,7 +876,7 @@
},
{
"cell_type": "code",
- "execution_count": 22,
+ "execution_count": 23,
"metadata": {},
"outputs": [
{
@@ -819,7 +1004,7 @@
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 24,
"metadata": {},
"outputs": [
{
@@ -828,7 +1013,7 @@
"True"
]
},
- "execution_count": 23,
+ "execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
@@ -846,7 +1031,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 25,
"metadata": {},
"outputs": [
{
@@ -855,7 +1040,7 @@
"False"
]
},
- "execution_count": 24,
+ "execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
@@ -866,7 +1051,7 @@
},
{
"cell_type": "code",
- "execution_count": 25,
+ "execution_count": 26,
"metadata": {},
"outputs": [
{
@@ -875,7 +1060,7 @@
"False"
]
},
- "execution_count": 25,
+ "execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
@@ -894,7 +1079,7 @@
},
{
"cell_type": "code",
- "execution_count": 26,
+ "execution_count": 27,
"metadata": {},
"outputs": [
{
@@ -903,7 +1088,7 @@
"True"
]
},
- "execution_count": 26,
+ "execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
@@ -932,7 +1117,7 @@
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 28,
"metadata": {},
"outputs": [
{
@@ -941,7 +1126,7 @@
"(True, False)"
]
},
- "execution_count": 27,
+ "execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
@@ -959,7 +1144,7 @@
},
{
"cell_type": "code",
- "execution_count": 28,
+ "execution_count": 29,
"metadata": {},
"outputs": [
{
@@ -968,7 +1153,7 @@
"(False, False)"
]
},
- "execution_count": 28,
+ "execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
@@ -1044,7 +1229,7 @@
},
{
"cell_type": "code",
- "execution_count": 29,
+ "execution_count": 30,
"metadata": {},
"outputs": [
{
@@ -1176,107 +1361,457 @@
"
\n",
"`distribute_and_over_or` distributes disjunctions over conjunctions.\n",
"
\n",
- "Run the cells below for implementation details.\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 30,
- "metadata": {},
- "outputs": [],
- "source": [
- "%psource eliminate_implications"
+ "Run the cell below for implementation details."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
- "outputs": [],
- "source": [
- "%psource move_not_inwards"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 32,
- "metadata": {},
- "outputs": [],
- "source": [
- "%psource distribute_and_over_or"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Let's convert some sentences to see how it works\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 33,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "((A | ~B) & (B | ~A))"
- ]
- },
- "execution_count": 33,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "A, B, C, D = expr('A, B, C, D')\n",
- "to_cnf(A |'<=>'| B)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 34,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "((A | ~B | ~C) & (B | ~A) & (C | ~A))"
- ]
- },
- "execution_count": 34,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "to_cnf(A |'<=>'| (B & C))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 35,
- "metadata": {},
"outputs": [
{
"data": {
- "text/plain": [
- "(A & (C | B) & (D | B))"
- ]
- },
- "execution_count": 35,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "to_cnf(A & (B | (C & D)))"
- ]
- },
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ " Codestin Search App\n",
+ " \n",
+ " \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "def eliminate_implications(s):\n",
+ " """Change implications into equivalent form with only &, |, and ~ as logical operators."""\n",
+ " s = expr(s)\n",
+ " if not s.args or is_symbol(s.op):\n",
+ " return s # Atoms are unchanged.\n",
+ " args = list(map(eliminate_implications, s.args))\n",
+ " a, b = args[0], args[-1]\n",
+ " if s.op == '==>':\n",
+ " return b | ~a\n",
+ " elif s.op == '<==':\n",
+ " return a | ~b\n",
+ " elif s.op == '<=>':\n",
+ " return (a | ~b) & (b | ~a)\n",
+ " elif s.op == '^':\n",
+ " assert len(args) == 2 # TODO: relax this restriction\n",
+ " return (a & ~b) | (~a & b)\n",
+ " else:\n",
+ " assert s.op in ('&', '|', '~')\n",
+ " return Expr(s.op, *args)\n",
+ "
\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ " Codestin Search App\n",
+ " \n",
+ " \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "def move_not_inwards(s):\n",
+ " """Rewrite sentence s by moving negation sign inward.\n",
+ " >>> move_not_inwards(~(A | B))\n",
+ " (~A & ~B)"""\n",
+ " s = expr(s)\n",
+ " if s.op == '~':\n",
+ " def NOT(b):\n",
+ " return move_not_inwards(~b)\n",
+ " a = s.args[0]\n",
+ " if a.op == '~':\n",
+ " return move_not_inwards(a.args[0]) # ~~A ==> A\n",
+ " if a.op == '&':\n",
+ " return associate('|', list(map(NOT, a.args)))\n",
+ " if a.op == '|':\n",
+ " return associate('&', list(map(NOT, a.args)))\n",
+ " return s\n",
+ " elif is_symbol(s.op) or not s.args:\n",
+ " return s\n",
+ " else:\n",
+ " return Expr(s.op, *list(map(move_not_inwards, s.args)))\n",
+ "
\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ " Codestin Search App\n",
+ " \n",
+ " \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "def distribute_and_over_or(s):\n",
+ " """Given a sentence s consisting of conjunctions and disjunctions\n",
+ " of literals, return an equivalent sentence in CNF.\n",
+ " >>> distribute_and_over_or((A & B) | C)\n",
+ " ((A | C) & (B | C))\n",
+ " """\n",
+ " s = expr(s)\n",
+ " if s.op == '|':\n",
+ " s = associate('|', s.args)\n",
+ " if s.op != '|':\n",
+ " return distribute_and_over_or(s)\n",
+ " if len(s.args) == 0:\n",
+ " return False\n",
+ " if len(s.args) == 1:\n",
+ " return distribute_and_over_or(s.args[0])\n",
+ " conj = first(arg for arg in s.args if arg.op == '&')\n",
+ " if not conj:\n",
+ " return s\n",
+ " others = [a for a in s.args if a is not conj]\n",
+ " rest = associate('|', others)\n",
+ " return associate('&', [distribute_and_over_or(c | rest)\n",
+ " for c in conj.args])\n",
+ " elif s.op == '&':\n",
+ " return associate('&', list(map(distribute_and_over_or, s.args)))\n",
+ " else:\n",
+ " return s\n",
+ "
\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "psource(eliminate_implications)\n",
+ "psource(move_not_inwards)\n",
+ "psource(distribute_and_over_or)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's convert some sentences to see how it works\n"
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 36,
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "((A | ~B) & (B | ~A))"
+ ]
+ },
+ "execution_count": 32,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "A, B, C, D = expr('A, B, C, D')\n",
+ "to_cnf(A |'<=>'| B)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "((A | ~B | ~C) & (B | ~A) & (C | ~A))"
+ ]
+ },
+ "execution_count": 33,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "to_cnf(A |'<=>'| (B & C))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "(A & (C | B) & (D | B))"
+ ]
+ },
+ "execution_count": 34,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "to_cnf(A & (B | (C & D)))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
"metadata": {},
"outputs": [
{
@@ -1285,7 +1820,7 @@
"((B | ~A | C | ~D) & (A | ~A | C | ~D) & (B | ~B | C | ~D) & (A | ~B | C | ~D))"
]
},
- "execution_count": 36,
+ "execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
@@ -1303,7 +1838,7 @@
},
{
"cell_type": "code",
- "execution_count": 37,
+ "execution_count": 36,
"metadata": {},
"outputs": [
{
@@ -1431,7 +1966,7 @@
},
{
"cell_type": "code",
- "execution_count": 38,
+ "execution_count": 37,
"metadata": {},
"outputs": [
{
@@ -1440,7 +1975,7 @@
"(True, False)"
]
},
- "execution_count": 38,
+ "execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
@@ -1451,7 +1986,7 @@
},
{
"cell_type": "code",
- "execution_count": 39,
+ "execution_count": 38,
"metadata": {},
"outputs": [
{
@@ -1460,7 +1995,7 @@
"(False, False)"
]
},
- "execution_count": 39,
+ "execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
@@ -1525,7 +2060,7 @@
},
{
"cell_type": "code",
- "execution_count": 40,
+ "execution_count": 39,
"metadata": {},
"outputs": [
{
@@ -1647,7 +2182,7 @@
},
{
"cell_type": "code",
- "execution_count": 41,
+ "execution_count": 40,
"metadata": {},
"outputs": [
{
@@ -1810,7 +2345,7 @@
},
{
"cell_type": "code",
- "execution_count": 42,
+ "execution_count": 41,
"metadata": {},
"outputs": [],
"source": [
@@ -1834,7 +2369,7 @@
},
{
"cell_type": "code",
- "execution_count": 43,
+ "execution_count": 42,
"metadata": {},
"outputs": [],
"source": [
@@ -1852,7 +2387,7 @@
},
{
"cell_type": "code",
- "execution_count": 44,
+ "execution_count": 43,
"metadata": {},
"outputs": [
{
@@ -1861,7 +2396,7 @@
"True"
]
},
- "execution_count": 44,
+ "execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
@@ -1872,7 +2407,7 @@
},
{
"cell_type": "code",
- "execution_count": 45,
+ "execution_count": 44,
"metadata": {},
"outputs": [
{
@@ -1881,7 +2416,7 @@
"True"
]
},
- "execution_count": 45,
+ "execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
@@ -1892,7 +2427,7 @@
},
{
"cell_type": "code",
- "execution_count": 46,
+ "execution_count": 45,
"metadata": {},
"outputs": [
{
@@ -1901,7 +2436,7 @@
"False"
]
},
- "execution_count": 46,
+ "execution_count": 45,
"metadata": {},
"output_type": "execute_result"
}
@@ -1912,7 +2447,7 @@
},
{
"cell_type": "code",
- "execution_count": 47,
+ "execution_count": 46,
"metadata": {},
"outputs": [
{
@@ -1921,7 +2456,7 @@
"False"
]
},
- "execution_count": 47,
+ "execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
@@ -1996,7 +2531,7 @@
},
{
"cell_type": "code",
- "execution_count": 48,
+ "execution_count": 47,
"metadata": {},
"outputs": [
{
@@ -2138,7 +2673,7 @@
},
{
"cell_type": "code",
- "execution_count": 49,
+ "execution_count": 48,
"metadata": {},
"outputs": [
{
@@ -2264,7 +2799,7 @@
},
{
"cell_type": "code",
- "execution_count": 50,
+ "execution_count": 49,
"metadata": {},
"outputs": [],
"source": [
@@ -2273,7 +2808,7 @@
},
{
"cell_type": "code",
- "execution_count": 51,
+ "execution_count": 50,
"metadata": {},
"outputs": [
{
@@ -2282,7 +2817,7 @@
"{A: True, B: True, C: False, D: True}"
]
},
- "execution_count": 51,
+ "execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
@@ -2300,16 +2835,16 @@
},
{
"cell_type": "code",
- "execution_count": 52,
+ "execution_count": 51,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "{B: True, D: False}"
+ "{B: True, C: True, D: False}"
]
},
- "execution_count": 52,
+ "execution_count": 51,
"metadata": {},
"output_type": "execute_result"
}
@@ -2329,7 +2864,7 @@
},
{
"cell_type": "code",
- "execution_count": 53,
+ "execution_count": 52,
"metadata": {},
"outputs": [
{
@@ -2338,7 +2873,7 @@
"{A: True, B: True}"
]
},
- "execution_count": 53,
+ "execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
@@ -2349,7 +2884,7 @@
},
{
"cell_type": "code",
- "execution_count": 54,
+ "execution_count": 53,
"metadata": {},
"outputs": [
{
@@ -2358,7 +2893,7 @@
"{A: False, B: True, C: True}"
]
},
- "execution_count": 54,
+ "execution_count": 53,
"metadata": {},
"output_type": "execute_result"
}
@@ -2369,7 +2904,7 @@
},
{
"cell_type": "code",
- "execution_count": 55,
+ "execution_count": 54,
"metadata": {},
"outputs": [
{
@@ -2378,7 +2913,7 @@
"{B: True, C: True}"
]
},
- "execution_count": 55,
+ "execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
@@ -2405,7 +2940,7 @@
},
{
"cell_type": "code",
- "execution_count": 56,
+ "execution_count": 55,
"metadata": {},
"outputs": [
{
@@ -2561,7 +3096,7 @@
},
{
"cell_type": "code",
- "execution_count": 57,
+ "execution_count": 56,
"metadata": {},
"outputs": [],
"source": [
@@ -2570,7 +3105,7 @@
},
{
"cell_type": "code",
- "execution_count": 58,
+ "execution_count": 57,
"metadata": {},
"outputs": [
{
@@ -2579,7 +3114,7 @@
"{A: True, B: True, C: False, D: True}"
]
},
- "execution_count": 58,
+ "execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
@@ -2597,7 +3132,7 @@
},
{
"cell_type": "code",
- "execution_count": 59,
+ "execution_count": 58,
"metadata": {},
"outputs": [
{
@@ -2606,7 +3141,7 @@
"{A: True, B: True, C: True}"
]
},
- "execution_count": 59,
+ "execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
@@ -2617,7 +3152,7 @@
},
{
"cell_type": "code",
- "execution_count": 60,
+ "execution_count": 59,
"metadata": {},
"outputs": [
{
@@ -2626,7 +3161,7 @@
"{A: True, B: True, C: True, D: True}"
]
},
- "execution_count": 60,
+ "execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
@@ -2637,7 +3172,7 @@
},
{
"cell_type": "code",
- "execution_count": 61,
+ "execution_count": 60,
"metadata": {},
"outputs": [],
"source": [
@@ -2662,7 +3197,7 @@
},
{
"cell_type": "code",
- "execution_count": 62,
+ "execution_count": 61,
"metadata": {},
"outputs": [],
"source": [
@@ -2679,16 +3214,16 @@
},
{
"cell_type": "code",
- "execution_count": 63,
+ "execution_count": 62,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
- "{A: False, B: False, C: True, D: False}"
+ "{A: True, B: True, C: False, D: True}"
]
},
- "execution_count": 63,
+ "execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
@@ -2712,7 +3247,7 @@
},
{
"cell_type": "code",
- "execution_count": 64,
+ "execution_count": 63,
"metadata": {},
"outputs": [],
"source": [
@@ -2723,14 +3258,14 @@
},
{
"cell_type": "code",
- "execution_count": 65,
+ "execution_count": 64,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "6.78 ms ± 238 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
+ "1.55 ms ± 64.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
@@ -2743,14 +3278,14 @@
},
{
"cell_type": "code",
- "execution_count": 66,
+ "execution_count": 65,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "4.64 ms ± 65.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
+ "1.02 ms ± 6.92 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
}
],
@@ -2796,7 +3331,7 @@
},
{
"cell_type": "code",
- "execution_count": 67,
+ "execution_count": 66,
"metadata": {},
"outputs": [
{
@@ -2991,7 +3526,7 @@
},
{
"cell_type": "code",
- "execution_count": 68,
+ "execution_count": 67,
"metadata": {},
"outputs": [
{
@@ -3024,7 +3559,7 @@
},
{
"cell_type": "code",
- "execution_count": 69,
+ "execution_count": 68,
"metadata": {},
"outputs": [
{
@@ -3066,10 +3601,8 @@
},
{
"cell_type": "code",
- "execution_count": 48,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 69,
+ "metadata": {},
"outputs": [],
"source": [
"clauses = []"
@@ -3095,10 +3628,8 @@
},
{
"cell_type": "code",
- "execution_count": 49,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 70,
+ "metadata": {},
"outputs": [],
"source": [
"clauses.append(expr(\"(American(x) & Weapon(y) & Sells(x, y, z) & Hostile(z)) ==> Criminal(x)\"))"
@@ -3116,10 +3647,8 @@
},
{
"cell_type": "code",
- "execution_count": 50,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 71,
+ "metadata": {},
"outputs": [],
"source": [
"clauses.append(expr(\"Enemy(Nono, America)\"))"
@@ -3137,10 +3666,8 @@
},
{
"cell_type": "code",
- "execution_count": 51,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 72,
+ "metadata": {},
"outputs": [],
"source": [
"clauses.append(expr(\"Owns(Nono, M1)\"))\n",
@@ -3161,10 +3688,8 @@
},
{
"cell_type": "code",
- "execution_count": 52,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 73,
+ "metadata": {},
"outputs": [],
"source": [
"clauses.append(expr(\"(Missile(x) & Owns(Nono, x)) ==> Sells(West, x, Nono)\"))"
@@ -3182,10 +3707,8 @@
},
{
"cell_type": "code",
- "execution_count": 53,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 74,
+ "metadata": {},
"outputs": [],
"source": [
"clauses.append(expr(\"American(West)\"))"
@@ -3202,10 +3725,8 @@
},
{
"cell_type": "code",
- "execution_count": 54,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 75,
+ "metadata": {},
"outputs": [],
"source": [
"clauses.append(expr(\"Missile(x) ==> Weapon(x)\"))\n",
@@ -3221,13 +3742,172 @@
},
{
"cell_type": "code",
- "execution_count": 55,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 76,
+ "metadata": {},
"outputs": [],
"source": [
- "crime_kb = FolKB(clauses)"
+ "crime_kb = FolKB(clauses)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The `subst` helper function substitutes variables with given values in first-order logic statements.\n",
+ "This will be useful in later algorithms.\n",
+ "It's implementation is quite simple and self-explanatory."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 77,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ " Codestin Search App\n",
+ " \n",
+ " \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "def subst(s, x):\n",
+ " """Substitute the substitution s into the expression x.\n",
+ " >>> subst({x: 42, y:0}, F(x) + y)\n",
+ " (F(42) + 0)\n",
+ " """\n",
+ " if isinstance(x, list):\n",
+ " return [subst(s, xi) for xi in x]\n",
+ " elif isinstance(x, tuple):\n",
+ " return tuple([subst(s, xi) for xi in x])\n",
+ " elif not isinstance(x, Expr):\n",
+ " return x\n",
+ " elif is_var_symbol(x.op):\n",
+ " return s.get(x, x)\n",
+ " else:\n",
+ " return Expr(x.op, *[subst(s, arg) for arg in x.args])\n",
+ "
\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "psource(subst)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Here's an example of how `subst` can be used."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 78,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "Owns(Nono, M1)"
+ ]
+ },
+ "execution_count": 78,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "subst({x: expr('Nono'), y: expr('M1')}, expr('Owns(x, y)'))"
]
},
{
@@ -3248,7 +3928,7 @@
},
{
"cell_type": "code",
- "execution_count": 56,
+ "execution_count": 79,
"metadata": {},
"outputs": [
{
@@ -3257,7 +3937,7 @@
"{x: 3}"
]
},
- "execution_count": 56,
+ "execution_count": 79,
"metadata": {},
"output_type": "execute_result"
}
@@ -3268,7 +3948,7 @@
},
{
"cell_type": "code",
- "execution_count": 57,
+ "execution_count": 80,
"metadata": {},
"outputs": [
{
@@ -3277,7 +3957,7 @@
"{x: B}"
]
},
- "execution_count": 57,
+ "execution_count": 80,
"metadata": {},
"output_type": "execute_result"
}
@@ -3288,7 +3968,7 @@
},
{
"cell_type": "code",
- "execution_count": 58,
+ "execution_count": 81,
"metadata": {},
"outputs": [
{
@@ -3297,7 +3977,7 @@
"{x: Bella, y: Dobby}"
]
},
- "execution_count": 58,
+ "execution_count": 81,
"metadata": {},
"output_type": "execute_result"
}
@@ -3315,7 +3995,7 @@
},
{
"cell_type": "code",
- "execution_count": 59,
+ "execution_count": 82,
"metadata": {},
"outputs": [
{
@@ -3339,7 +4019,7 @@
},
{
"cell_type": "code",
- "execution_count": 60,
+ "execution_count": 83,
"metadata": {},
"outputs": [
{
@@ -3366,7 +4046,7 @@
},
{
"cell_type": "code",
- "execution_count": 61,
+ "execution_count": 84,
"metadata": {},
"outputs": [
{
@@ -3516,7 +4196,7 @@
},
{
"cell_type": "code",
- "execution_count": 62,
+ "execution_count": 85,
"metadata": {},
"outputs": [
{
@@ -3541,7 +4221,7 @@
},
{
"cell_type": "code",
- "execution_count": 63,
+ "execution_count": 86,
"metadata": {},
"outputs": [
{
@@ -3583,13 +4263,117 @@
},
{
"cell_type": "code",
- "execution_count": 64,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
+ "execution_count": 87,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ " Codestin Search App\n",
+ " \n",
+ " \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "def fol_bc_or(KB, goal, theta):\n",
+ " for rule in KB.fetch_rules_for_goal(goal):\n",
+ " lhs, rhs = parse_definite_clause(standardize_variables(rule))\n",
+ " for theta1 in fol_bc_and(KB, lhs, unify(rhs, goal, theta)):\n",
+ " yield theta1\n",
+ "
\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
- "%psource fol_bc_or"
+ "psource(fol_bc_or)"
]
},
{
@@ -3602,13 +4386,122 @@
},
{
"cell_type": "code",
- "execution_count": 65,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
+ "execution_count": 88,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ " Codestin Search App\n",
+ " \n",
+ " \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "def fol_bc_and(KB, goals, theta):\n",
+ " if theta is None:\n",
+ " pass\n",
+ " elif not goals:\n",
+ " yield theta\n",
+ " else:\n",
+ " first, rest = goals[0], goals[1:]\n",
+ " for theta1 in fol_bc_or(KB, subst(theta, first), theta):\n",
+ " for theta2 in fol_bc_and(KB, rest, theta1):\n",
+ " yield theta2\n",
+ "
\n",
+ "\n",
+ "\n"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
"source": [
- "%psource fol_bc_and"
+ "psource(fol_bc_and)"
]
},
{
@@ -3620,10 +4513,8 @@
},
{
"cell_type": "code",
- "execution_count": 66,
- "metadata": {
- "collapsed": true
- },
+ "execution_count": 89,
+ "metadata": {},
"outputs": [],
"source": [
"# Rebuild KB because running fol_fc_ask would add new facts to the KB\n",
@@ -3632,7 +4523,7 @@
},
{
"cell_type": "code",
- "execution_count": 67,
+ "execution_count": 90,
"metadata": {},
"outputs": [
{
@@ -3641,7 +4532,7 @@
"{v_5: x, x: Nono}"
]
},
- "execution_count": 67,
+ "execution_count": 90,
"metadata": {},
"output_type": "execute_result"
}
@@ -3668,7 +4559,7 @@
},
{
"cell_type": "code",
- "execution_count": 68,
+ "execution_count": 91,
"metadata": {},
"outputs": [
{
@@ -3677,7 +4568,7 @@
"(P ==> ~Q)"
]
},
- "execution_count": 68,
+ "execution_count": 91,
"metadata": {},
"output_type": "execute_result"
}
@@ -3695,7 +4586,7 @@
},
{
"cell_type": "code",
- "execution_count": 69,
+ "execution_count": 92,
"metadata": {},
"outputs": [
{
@@ -3704,7 +4595,7 @@
"(P ==> ~Q)"
]
},
- "execution_count": 69,
+ "execution_count": 92,
"metadata": {},
"output_type": "execute_result"
}
@@ -3722,7 +4613,7 @@
},
{
"cell_type": "code",
- "execution_count": 70,
+ "execution_count": 93,
"metadata": {},
"outputs": [
{
@@ -3731,7 +4622,7 @@
"PartialExpr('==>', P)"
]
},
- "execution_count": 70,
+ "execution_count": 93,
"metadata": {},
"output_type": "execute_result"
}
@@ -3751,7 +4642,7 @@
},
{
"cell_type": "code",
- "execution_count": 71,
+ "execution_count": 94,
"metadata": {},
"outputs": [
{
@@ -3760,7 +4651,7 @@
"(P ==> ~Q)"
]
},
- "execution_count": 71,
+ "execution_count": 94,
"metadata": {},
"output_type": "execute_result"
}
@@ -3790,7 +4681,7 @@
},
{
"cell_type": "code",
- "execution_count": 72,
+ "execution_count": 95,
"metadata": {},
"outputs": [
{
@@ -3799,7 +4690,7 @@
"(~(P & Q) ==> (~P | ~Q))"
]
},
- "execution_count": 72,
+ "execution_count": 95,
"metadata": {},
"output_type": "execute_result"
}
@@ -3817,7 +4708,7 @@
},
{
"cell_type": "code",
- "execution_count": 73,
+ "execution_count": 96,
"metadata": {},
"outputs": [
{
@@ -3826,7 +4717,7 @@
"(~(P & Q) ==> (~P | ~Q))"
]
},
- "execution_count": 73,
+ "execution_count": 96,
"metadata": {},
"output_type": "execute_result"
}
@@ -3845,7 +4736,7 @@
},
{
"cell_type": "code",
- "execution_count": 74,
+ "execution_count": 97,
"metadata": {},
"outputs": [
{
@@ -3854,7 +4745,7 @@
"(((P & Q) ==> P) | Q)"
]
},
- "execution_count": 74,
+ "execution_count": 97,
"metadata": {},
"output_type": "execute_result"
}
@@ -3872,7 +4763,7 @@
},
{
"cell_type": "code",
- "execution_count": 75,
+ "execution_count": 98,
"metadata": {},
"outputs": [
{
@@ -3881,7 +4772,7 @@
"((P & Q) ==> (P | Q))"
]
},
- "execution_count": 75,
+ "execution_count": 98,
"metadata": {},
"output_type": "execute_result"
}
@@ -3899,7 +4790,7 @@
},
{
"cell_type": "code",
- "execution_count": 76,
+ "execution_count": 99,
"metadata": {},
"outputs": [
{
diff --git a/logic.py b/logic.py
index dfa70d0db..a1a025293 100644
--- a/logic.py
+++ b/logic.py
@@ -133,17 +133,26 @@ def make_action_sentence(action, t):
def is_symbol(s):
- """A string s is a symbol if it starts with an alphabetic char."""
+ """A string s is a symbol if it starts with an alphabetic char.
+ >>> is_symbol('R2D2')
+ True
+ """
return isinstance(s, str) and s[:1].isalpha()
def is_var_symbol(s):
- """A logic variable symbol is an initial-lowercase string."""
+ """A logic variable symbol is an initial-lowercase string.
+ >>> is_var_symbol('EXE')
+ False
+ """
return is_symbol(s) and s[0].islower()
def is_prop_symbol(s):
- """A proposition logic symbol is an initial-uppercase string."""
+ """A proposition logic symbol is an initial-uppercase string.
+ >>> is_prop_symbol('exe')
+ False
+ """
return is_symbol(s) and s[0].isupper()
@@ -259,7 +268,10 @@ def pl_true(exp, model={}):
"""Return True if the propositional logic expression is true in the model,
and False if it is false. If the model does not specify the value for
every proposition, this may return None to indicate 'not obvious';
- this may happen even when the expression is tautological."""
+ this may happen even when the expression is tautological.
+ >>> pl_true(P, {}) is None
+ True
+ """
if exp in (True, False):
return exp
op, args = exp.op, exp.args
@@ -350,7 +362,8 @@ def eliminate_implications(s):
def move_not_inwards(s):
"""Rewrite sentence s by moving negation sign inward.
>>> move_not_inwards(~(A | B))
- (~A & ~B)"""
+ (~A & ~B)
+ """
s = expr(s)
if s.op == '~':
def NOT(b):
@@ -420,7 +433,10 @@ def associate(op, args):
def dissociate(op, args):
"""Given an associative op, return a flattened list result such
- that Expr(op, *result) means the same as Expr(op, *args)."""
+ that Expr(op, *result) means the same as Expr(op, *args).
+ >>> dissociate('&', [A & B])
+ [A, B]
+ """
result = []
def collect(subargs):
@@ -456,7 +472,10 @@ def disjuncts(s):
def pl_resolution(KB, alpha):
- """Propositional-logic resolution: say if alpha follows from KB. [Figure 7.12]"""
+ """Propositional-logic resolution: say if alpha follows from KB. [Figure 7.12]
+ >>> pl_resolution(horn_clauses_KB, A)
+ True
+ """
clauses = KB.clauses + conjuncts(to_cnf(~alpha))
new = set()
while True:
@@ -549,6 +568,13 @@ def pl_fc_entails(KB, q):
for s in "P==>Q; (L&M)==>P; (B&L)==>M; (A&P)==>L; (A&B)==>L; A;B".split(';'):
horn_clauses_KB.tell(expr(s))
+"""
+Definite clauses KB example
+"""
+definite_clauses_KB = PropDefiniteKB()
+for clause in ['(B & F)==>E', '(A & E & F)==>G', '(B & C)==>F', '(A & B)==>D', '(E & F)==>H', '(H & I)==>J', 'A', 'B', 'C']:
+ definite_clauses_KB.tell(expr(clause))
+
# ______________________________________________________________________________
# DPLL-Satisfiable [Figure 7.17]
@@ -558,7 +584,10 @@ def dpll_satisfiable(s):
This differs from the book code in two ways: (1) it returns a model
rather than True when it succeeds; this is more useful. (2) The
function find_pure_symbol is passed a list of unknown clauses, rather
- than a list of all clauses and the model; this is more efficient."""
+ than a list of all clauses and the model; this is more efficient.
+ >>> dpll_satisfiable(A |'<=>'| B) == {A: True, B: True}
+ True
+ """
clauses = conjuncts(to_cnf(s))
symbols = list(prop_symbols(s))
return dpll(clauses, symbols, {})
@@ -661,6 +690,8 @@ def inspect_literal(literal):
def WalkSAT(clauses, p=0.5, max_flips=10000):
"""Checks for satisfiability of all clauses by randomly flipping values of variables
+ >>> WalkSAT([A & ~A], 0.5, 100) is None
+ True
"""
# Set of all symbols in all clauses
symbols = {sym for clause in clauses for sym in prop_symbols(clause)}
@@ -1141,7 +1172,11 @@ def plan_shot(self, current, goals, allowed):
def SAT_plan(init, transition, goal, t_max, SAT_solver=dpll_satisfiable):
"""Converts a planning problem to Satisfaction problem by translating it to a cnf sentence.
- [Figure 7.22]"""
+ [Figure 7.22]
+ >>> transition = {'A': {'Left': 'A', 'Right': 'B'}, 'B': {'Left': 'A', 'Right': 'C'}, 'C': {'Left': 'B', 'Right': 'C'}}
+ >>> SAT_plan('A', transition, 'C', 2) is None
+ True
+ """
# Functions used by SAT_plan
def translate_to_SAT(init, transition, goal, time):
@@ -1225,7 +1260,10 @@ def extract_solution(model):
def unify(x, y, s={}):
"""Unify expressions x,y with substitution s; return a substitution that
would make x,y equal, or None if x,y can not unify. x and y can be
- variables (e.g. Expr('x')), constants, lists, or Exprs. [Figure 9.1]"""
+ variables (e.g. Expr('x')), constants, lists, or Exprs. [Figure 9.1]
+ >>> unify(x, 3, {})
+ {x: 3}
+ """
if s is None:
return None
elif x == y:
@@ -1279,7 +1317,10 @@ def occur_check(var, x, s):
def extend(s, var, val):
- """Copy the substitution s and extend it by setting var to val; return copy."""
+ """Copy the substitution s and extend it by setting var to val; return copy.
+ >>> extend({x: 1}, y, 2) == {x: 1, y: 2}
+ True
+ """
s2 = s.copy()
s2[var] = val
return s2
@@ -1560,5 +1601,8 @@ def simp(x):
def d(y, x):
- """Differentiate and then simplify."""
+ """Differentiate and then simplify.
+ >>> d(x * x - x, x)
+ ((2 * x) - 1)
+ """
return simp(diff(y, x))
diff --git a/tests/test_agents.py b/tests/test_agents.py
index ded9b7d95..dd390fc89 100644
--- a/tests/test_agents.py
+++ b/tests/test_agents.py
@@ -3,7 +3,7 @@
from agents import Agent
from agents import ReflexVacuumAgent, ModelBasedVacuumAgent, TrivialVacuumEnvironment, compare_agents,\
RandomVacuumAgent, TableDrivenVacuumAgent, TableDrivenAgentProgram, RandomAgentProgram, \
- SimpleReflexAgentProgram, rule_match
+ SimpleReflexAgentProgram, ModelBasedReflexAgentProgram, rule_match
random.seed("aima-python")
@@ -55,6 +55,7 @@ def test_add():
assert l1.direction == Direction.U
assert l2.direction == Direction.D
+
def test_RandomAgentProgram() :
#create a list of all the actions a vacuum cleaner can perform
list = ['Right', 'Left', 'Suck', 'NoOp']
@@ -71,6 +72,7 @@ def test_RandomAgentProgram() :
# check final status of the environment
assert environment.status == {(1, 0): 'Clean' , (0, 0): 'Clean'}
+
def test_RandomVacuumAgent() :
# create an object of the RandomVacuumAgent
agent = RandomVacuumAgent()
@@ -132,6 +134,7 @@ def test_ReflexVacuumAgent() :
# check final status of the environment
assert environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}
+
def test_SimpleReflexAgentProgram():
class Rule:
@@ -165,6 +168,39 @@ def interpret_input(state):
assert environment.status == {(1,0):'Clean' , (0,0) : 'Clean'}
+def test_ModelBasedReflexAgentProgram():
+ class Rule:
+
+ def __init__(self, state, action):
+ self.__state = state
+ self.action = action
+
+ def matches(self, state):
+ return self.__state == state
+
+ loc_A = (0, 0)
+ loc_B = (1, 0)
+
+ # create rules for a two-state vacuum environment
+ rules = [Rule((loc_A, "Dirty"), "Suck"), Rule((loc_A, "Clean"), "Right"),
+ Rule((loc_B, "Dirty"), "Suck"), Rule((loc_B, "Clean"), "Left")]
+
+ def update_state(state, action, percept, model):
+ return percept
+
+ # create a program and then an object of the ModelBasedReflexAgentProgram class
+ program = ModelBasedReflexAgentProgram(rules, update_state, None)
+ agent = Agent(program)
+ # create an object of TrivialVacuumEnvironment
+ environment = TrivialVacuumEnvironment()
+ # add agent to the environment
+ environment.add_thing(agent)
+ # run the environment
+ environment.run()
+ # check final status of the environment
+ assert environment.status == {(1, 0): 'Clean', (0, 0): 'Clean'}
+
+
def test_ModelBasedVacuumAgent() :
# create an object of the ModelBasedVacuumAgent
agent = ModelBasedVacuumAgent()