From 0f74864d94970c2da65f9103c984dc3e3cc10cf3 Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 18:55:59 +0530 Subject: [PATCH 01/16] Added KB_AgentProgram and subst --- logic.ipynb | 1375 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 1133 insertions(+), 242 deletions(-) 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": [ { From 807106cbd1888bb52987553480e3ec61504e35cd Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 19:22:04 +0530 Subject: [PATCH 02/16] Added doctests --- logic.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/logic.py b/logic.py index dfa70d0db..9a27b3ec3 100644 --- a/logic.py +++ b/logic.py @@ -133,17 +133,32 @@ 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 + >>> is_symbol('42D') + False + """ 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 + >>> is_var_symbol('eXE') + True + """ 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 + >>> is_prop_symbol('EXE') + True + """ return is_symbol(s) and s[0].isupper() @@ -151,6 +166,8 @@ def variables(s): """Return a set of the variables in expression s. >>> variables(expr('F(x, x) & G(x, y) & H(y, z) & R(A, z, 2)')) == {x, y, z} True + >>> variables(expr('(p <=> q) & R(p, q) & 42')) + {p, q} """ return {x for x in subexpressions(s) if is_variable(x)} @@ -250,6 +267,8 @@ def tt_true(s): """Is a propositional sentence a tautology? >>> tt_true('P | ~P') True + >>> tt_true('~(A & B) <=> (~A | ~B)') + True """ s = expr(s) return tt_entails(True, s) @@ -259,7 +278,12 @@ 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 + >>> pl_true(P | Q, {P: True}) + True + """ if exp in (True, False): return exp op, args = exp.op, exp.args @@ -317,6 +341,8 @@ def to_cnf(s): That is, to the form ((A | ~B | ...) & (B | C | ...) & ...) [p. 253] >>> to_cnf('~(B | C)') (~B & ~C) + >>> to_cnf('A <=> B') + ((A | ~B) & (B | ~A)) """ s = expr(s) if isinstance(s, str): @@ -350,7 +376,10 @@ 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) + >>> move_not_inwards(~(~(A | ~B) | ~~C)) + ((A | ~B) & ~C) + """ s = expr(s) if s.op == '~': def NOT(b): @@ -420,7 +449,12 @@ 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] + >>> dissociate('&', [A, B, C & D, P | Q]) + [A, B, C, D, P | Q] + """ result = [] def collect(subargs): @@ -456,7 +490,12 @@ 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 + pl_resolution(definite_clauses_KB, P) + False + """ clauses = KB.clauses + conjuncts(to_cnf(~alpha)) new = set() while True: @@ -517,6 +556,10 @@ def pl_fc_entails(KB, q): [Figure 7.15] >>> pl_fc_entails(horn_clauses_KB, expr('Q')) True + >>> pl_fc_entails(definite_clauses_KB, expr('G')) + True + >>> pl_fc_entails(definite_clauses_KB, expr('I')) + False """ count = {c: len(conjuncts(c.args[0])) for c in KB.clauses @@ -558,7 +601,12 @@ 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} + >>> dpll_satisfiable(P & ~P) + False + """ clauses = conjuncts(to_cnf(s)) symbols = list(prop_symbols(s)) return dpll(clauses, symbols, {}) @@ -661,6 +709,10 @@ 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 + >>> WalkSAT([A | B, B & C, C | D, D & A, P, ~P], 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 +1193,13 @@ 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 + >>> SAT_plan('A', transition, 'B', 3) + ['Right'] + """ # Functions used by SAT_plan def translate_to_SAT(init, transition, goal, time): @@ -1225,7 +1283,12 @@ 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} + >>> unify(expr('A(x)'), expr('A(B)')) + {x: B} + """ if s is None: return None elif x == y: @@ -1279,7 +1342,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} + """ s2 = s.copy() s2[var] = val return s2 @@ -1560,5 +1626,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)) From ea5e283b897974c917cdc6775df27de268c9785f Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 19:25:35 +0530 Subject: [PATCH 03/16] Updated README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8bac287b6..bc1e2fc76 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,8 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and | 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 | From 509241fd75ee5d282b33b541c9ce3a99b175167e Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 19:34:08 +0530 Subject: [PATCH 04/16] Fixed doctest --- logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic.py b/logic.py index 9a27b3ec3..abb36cdf0 100644 --- a/logic.py +++ b/logic.py @@ -1628,6 +1628,6 @@ def simp(x): def d(y, x): """Differentiate and then simplify. >>> d(x * x - x, x) - 2 * x - 1 + ((2 * x) - 1) """ return simp(diff(y, x)) From 5bd5bec76508608d330c51856f08b1ecff27dd31 Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 19:38:09 +0530 Subject: [PATCH 05/16] Fixed doctest --- logic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logic.py b/logic.py index abb36cdf0..1e497c7a7 100644 --- a/logic.py +++ b/logic.py @@ -602,8 +602,8 @@ def dpll_satisfiable(s): 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. - >>> dpll_satisfiable(A |'<=>'| B) - {A: True, B: True} + >>> dpll_satisfiable(A |'<=>'| B) == {A: True, B: True} + True >>> dpll_satisfiable(P & ~P) False """ From b2833f005e52f30ac210fc2bca6f0640522aa84f Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 19:41:43 +0530 Subject: [PATCH 06/16] Fixed doctest --- logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic.py b/logic.py index 1e497c7a7..fb6b2ff12 100644 --- a/logic.py +++ b/logic.py @@ -453,7 +453,7 @@ def dissociate(op, args): >>> dissociate('&', [A & B]) [A, B] >>> dissociate('&', [A, B, C & D, P | Q]) - [A, B, C, D, P | Q] + [A, B, C, D, (P | Q)] """ result = [] From 87855b97f8127139a34614df5c9c6c61c4f264af Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 19:46:47 +0530 Subject: [PATCH 07/16] Added definite_clauses_KB to logic.py --- logic.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/logic.py b/logic.py index fb6b2ff12..c37bd5041 100644 --- a/logic.py +++ b/logic.py @@ -592,6 +592,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] From aa651b1d5d9db5b8c892dcb5d7adb5cf70b5f816 Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 19:52:37 +0530 Subject: [PATCH 08/16] Fixed a doctest, again --- logic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logic.py b/logic.py index c37bd5041..61c37a984 100644 --- a/logic.py +++ b/logic.py @@ -1350,8 +1350,8 @@ 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. - >>> extend({x: 1}, y, 2) - {x: 1, y: 2} + >>> extend({x: 1}, y, 2) == {x: 1, y: 2} + True """ s2 = s.copy() s2[var] = val From 8503ea29afcbfaa84695534b467db02988959175 Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 19:57:12 +0530 Subject: [PATCH 09/16] Fixed another doctest --- logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic.py b/logic.py index 61c37a984..36cc31f84 100644 --- a/logic.py +++ b/logic.py @@ -493,7 +493,7 @@ def pl_resolution(KB, alpha): """Propositional-logic resolution: say if alpha follows from KB. [Figure 7.12] >>> pl_resolution(horn_clauses_KB, A) True - pl_resolution(definite_clauses_KB, P) + >>> pl_resolution(definite_clauses_KB, P) False """ clauses = KB.clauses + conjuncts(to_cnf(~alpha)) From 3d6265ff7ea48e4621ccdb59594e4c092758335d Mon Sep 17 00:00:00 2001 From: ad71 Date: Thu, 28 Jun 2018 20:03:27 +0530 Subject: [PATCH 10/16] Fixed another doctest --- logic.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/logic.py b/logic.py index 36cc31f84..a5719285d 100644 --- a/logic.py +++ b/logic.py @@ -493,8 +493,6 @@ def pl_resolution(KB, alpha): """Propositional-logic resolution: say if alpha follows from KB. [Figure 7.12] >>> pl_resolution(horn_clauses_KB, A) True - >>> pl_resolution(definite_clauses_KB, P) - False """ clauses = KB.clauses + conjuncts(to_cnf(~alpha)) new = set() From b549c2019f32cb2d6d9bf76c7f6547533e5dd241 Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Fri, 3 Aug 2018 18:51:40 +0530 Subject: [PATCH 11/16] Moved unnecessary doctests to unit tests --- logic.py | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/logic.py b/logic.py index a5719285d..e5cc18adb 100644 --- a/logic.py +++ b/logic.py @@ -136,8 +136,6 @@ def is_symbol(s): """A string s is a symbol if it starts with an alphabetic char. >>> is_symbol('R2D2') True - >>> is_symbol('42D') - False """ return isinstance(s, str) and s[:1].isalpha() @@ -146,8 +144,6 @@ def is_var_symbol(s): """A logic variable symbol is an initial-lowercase string. >>> is_var_symbol('EXE') False - >>> is_var_symbol('eXE') - True """ return is_symbol(s) and s[0].islower() @@ -156,16 +152,12 @@ def is_prop_symbol(s): """A proposition logic symbol is an initial-uppercase string. >>> is_prop_symbol('exe') False - >>> is_prop_symbol('EXE') - True """ return is_symbol(s) and s[0].isupper() def variables(s): """Return a set of the variables in expression s. - >>> variables(expr('F(x, x) & G(x, y) & H(y, z) & R(A, z, 2)')) == {x, y, z} - True >>> variables(expr('(p <=> q) & R(p, q) & 42')) {p, q} """ @@ -267,8 +259,6 @@ def tt_true(s): """Is a propositional sentence a tautology? >>> tt_true('P | ~P') True - >>> tt_true('~(A & B) <=> (~A | ~B)') - True """ s = expr(s) return tt_entails(True, s) @@ -281,8 +271,6 @@ def pl_true(exp, model={}): this may happen even when the expression is tautological. >>> pl_true(P, {}) is None True - >>> pl_true(P | Q, {P: True}) - True """ if exp in (True, False): return exp @@ -341,8 +329,6 @@ def to_cnf(s): That is, to the form ((A | ~B | ...) & (B | C | ...) & ...) [p. 253] >>> to_cnf('~(B | C)') (~B & ~C) - >>> to_cnf('A <=> B') - ((A | ~B) & (B | ~A)) """ s = expr(s) if isinstance(s, str): @@ -377,8 +363,6 @@ def move_not_inwards(s): """Rewrite sentence s by moving negation sign inward. >>> move_not_inwards(~(A | B)) (~A & ~B) - >>> move_not_inwards(~(~(A | ~B) | ~~C)) - ((A | ~B) & ~C) """ s = expr(s) if s.op == '~': @@ -452,8 +436,6 @@ def dissociate(op, args): that Expr(op, *result) means the same as Expr(op, *args). >>> dissociate('&', [A & B]) [A, B] - >>> dissociate('&', [A, B, C & D, P | Q]) - [A, B, C, D, (P | Q)] """ result = [] @@ -554,10 +536,6 @@ def pl_fc_entails(KB, q): [Figure 7.15] >>> pl_fc_entails(horn_clauses_KB, expr('Q')) True - >>> pl_fc_entails(definite_clauses_KB, expr('G')) - True - >>> pl_fc_entails(definite_clauses_KB, expr('I')) - False """ count = {c: len(conjuncts(c.args[0])) for c in KB.clauses @@ -609,8 +587,6 @@ def dpll_satisfiable(s): than a list of all clauses and the model; this is more efficient. >>> dpll_satisfiable(A |'<=>'| B) == {A: True, B: True} True - >>> dpll_satisfiable(P & ~P) - False """ clauses = conjuncts(to_cnf(s)) symbols = list(prop_symbols(s)) @@ -716,8 +692,6 @@ 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 - >>> WalkSAT([A | B, B & C, C | D, D & A, P, ~P], 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)} @@ -1202,8 +1176,6 @@ def SAT_plan(init, transition, goal, t_max, SAT_solver=dpll_satisfiable): >>> 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 - >>> SAT_plan('A', transition, 'B', 3) - ['Right'] """ # Functions used by SAT_plan @@ -1291,8 +1263,6 @@ def unify(x, y, s={}): variables (e.g. Expr('x')), constants, lists, or Exprs. [Figure 9.1] >>> unify(x, 3, {}) {x: 3} - >>> unify(expr('A(x)'), expr('A(B)')) - {x: B} """ if s is None: return None From 236f57af26a52ae4056652d216100e7881a87826 Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Sun, 5 Aug 2018 15:57:17 +0530 Subject: [PATCH 12/16] Added unit test for ModelBasedReflexAgent --- agents.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, From ed70d850f2127e6c8e7ba30d4e1db891dc22a9cb Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Sun, 5 Aug 2018 15:58:08 +0530 Subject: [PATCH 13/16] Added unit test for ModelBasedReflexAgent --- tests/test_agents.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/tests/test_agents.py b/tests/test_agents.py index ded9b7d95..4c914ca62 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 + 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() From 531567309138afe76fc55a38a064081e469aaac2 Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Sun, 5 Aug 2018 15:58:43 +0530 Subject: [PATCH 14/16] Updated README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc1e2fc76..bec48d417 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ 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 | From da7c49eee3cddcda01b4b143179a3e83be27f5ff Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Sun, 5 Aug 2018 16:08:04 +0530 Subject: [PATCH 15/16] Minor fix --- tests/test_agents.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_agents.py b/tests/test_agents.py index 4c914ca62..dd390fc89 100644 --- a/tests/test_agents.py +++ b/tests/test_agents.py @@ -188,7 +188,7 @@ def matches(self, state): def update_state(state, action, percept, model): return percept - # create a program and then an object of the ModelBasedReflexAgentProgram + # 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 From cf84e4b197518330626835b109a343c8225cb528 Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Sun, 5 Aug 2018 16:25:27 +0530 Subject: [PATCH 16/16] Fixed a doctest --- logic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logic.py b/logic.py index e5cc18adb..a1a025293 100644 --- a/logic.py +++ b/logic.py @@ -158,8 +158,8 @@ def is_prop_symbol(s): def variables(s): """Return a set of the variables in expression s. - >>> variables(expr('(p <=> q) & R(p, q) & 42')) - {p, q} + >>> variables(expr('F(x, x) & G(x, y) & H(y, z) & R(A, z, 2)')) == {x, y, z} + True """ return {x for x in subexpressions(s) if is_variable(x)}