|
13 | 13 | "cell_type": "markdown",
|
14 | 14 | "metadata": {},
|
15 | 15 | "source": [
|
16 |
| - "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", |
| 16 | + "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 of 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", |
17 | 17 | "\n",
|
18 | 18 | "Let's first import everything from the `logic` module."
|
19 | 19 | ]
|
20 | 20 | },
|
21 | 21 | {
|
22 | 22 | "cell_type": "code",
|
23 | 23 | "execution_count": 1,
|
24 |
| - "metadata": {}, |
| 24 | + "metadata": { |
| 25 | + "collapsed": true |
| 26 | + }, |
25 | 27 | "outputs": [],
|
26 | 28 | "source": [
|
27 | 29 | "from utils import *\n",
|
|
98 | 100 | {
|
99 | 101 | "cell_type": "code",
|
100 | 102 | "execution_count": 3,
|
101 |
| - "metadata": {}, |
| 103 | + "metadata": { |
| 104 | + "collapsed": true |
| 105 | + }, |
102 | 106 | "outputs": [],
|
103 | 107 | "source": [
|
104 | 108 | "(x, y, P, Q, f) = symbols('x, y, P, Q, f')"
|
|
426 | 430 | {
|
427 | 431 | "cell_type": "code",
|
428 | 432 | "execution_count": 15,
|
429 |
| - "metadata": {}, |
| 433 | + "metadata": { |
| 434 | + "collapsed": true |
| 435 | + }, |
430 | 436 | "outputs": [],
|
431 | 437 | "source": [
|
432 | 438 | "wumpus_kb = PropKB()"
|
|
444 | 450 | {
|
445 | 451 | "cell_type": "code",
|
446 | 452 | "execution_count": 16,
|
447 |
| - "metadata": {}, |
| 453 | + "metadata": { |
| 454 | + "collapsed": true |
| 455 | + }, |
448 | 456 | "outputs": [],
|
449 | 457 | "source": [
|
450 | 458 | "P11, P12, P21, P22, P31, B11, B21 = expr('P11, P12, P21, P22, P31, B11, B21')"
|
|
461 | 469 | {
|
462 | 470 | "cell_type": "code",
|
463 | 471 | "execution_count": 17,
|
464 |
| - "metadata": {}, |
| 472 | + "metadata": { |
| 473 | + "collapsed": true |
| 474 | + }, |
465 | 475 | "outputs": [],
|
466 | 476 | "source": [
|
467 | 477 | "wumpus_kb.tell(~P11)"
|
|
477 | 487 | {
|
478 | 488 | "cell_type": "code",
|
479 | 489 | "execution_count": 18,
|
480 |
| - "metadata": {}, |
| 490 | + "metadata": { |
| 491 | + "collapsed": true |
| 492 | + }, |
481 | 493 | "outputs": [],
|
482 | 494 | "source": [
|
483 | 495 | "wumpus_kb.tell(B11 | '<=>' | ((P12 | P21)))\n",
|
|
494 | 506 | {
|
495 | 507 | "cell_type": "code",
|
496 | 508 | "execution_count": 19,
|
497 |
| - "metadata": {}, |
| 509 | + "metadata": { |
| 510 | + "collapsed": true |
| 511 | + }, |
498 | 512 | "outputs": [],
|
499 | 513 | "source": [
|
500 | 514 | "wumpus_kb.tell(~B11)\n",
|
|
564 | 578 | "<br>\n",
|
565 | 579 | "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",
|
566 | 580 | "<br>\n",
|
567 |
| - "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", |
| 581 | + "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 in fact taken that action.\n", |
568 | 582 | "<br>\n",
|
569 | 583 | "Our implementation of `KB-Agent` is encapsulated in a class `KB_AgentProgram` which inherits from the `KB` class.\n",
|
570 | 584 | "<br>\n",
|
|
1168 | 1182 | "source": [
|
1169 | 1183 | "### Proof by Resolution\n",
|
1170 | 1184 | "Recall that our goal is to check whether $\\text{KB} \\vDash \\alpha$ i.e. is $\\text{KB} \\implies \\alpha$ true in every model. Suppose we wanted to check if $P \\implies Q$ is valid. We check the satisfiability of $\\neg (P \\implies Q)$, which can be rewritten as $P \\land \\neg Q$. If $P \\land \\neg Q$ is unsatisfiable, then $P \\implies Q$ must be true in all models. This gives us the result \"$\\text{KB} \\vDash \\alpha$ <em>if and only if</em> $\\text{KB} \\land \\neg \\alpha$ is unsatisfiable\".<br/>\n",
|
1171 |
| - "This technique corresponds to <em>proof by <strong>contradiction</strong></em>, a standard mathematical proof technique. We assume $\\alpha$ to be false and show that this leads to a contradiction with known axioms in $\\text{KB}$. We obtain a contradiction by making valid inferences using inference rules. In this proof we use a single inference rule, <strong>resolution</strong> which states $(l_1 \\lor \\dots \\lor l_k) \\land (m_1 \\lor \\dots \\lor m_n) \\land (l_i \\iff \\neg m_j) \\implies l_1 \\lor \\dots \\lor l_{i - 1} \\lor l_{i + 1} \\lor \\dots \\lor l_k \\lor m_1 \\lor \\dots \\lor m_{j - 1} \\lor m_{j + 1} \\lor \\dots \\lor m_n$. Applying the resolution yeilds us a clause which we add to the KB. We keep doing this until:\n", |
| 1185 | + "This technique corresponds to <em>proof by <strong>contradiction</strong></em>, a standard mathematical proof technique. We assume $\\alpha$ to be false and show that this leads to a contradiction with known axioms in $\\text{KB}$. We obtain a contradiction by making valid inferences using inference rules. In this proof we use a single inference rule, <strong>resolution</strong> which states $(l_1 \\lor \\dots \\lor l_k) \\land (m_1 \\lor \\dots \\lor m_n) \\land (l_i \\iff \\neg m_j) \\implies l_1 \\lor \\dots \\lor l_{i - 1} \\lor l_{i + 1} \\lor \\dots \\lor l_k \\lor m_1 \\lor \\dots \\lor m_{j - 1} \\lor m_{j + 1} \\lor \\dots \\lor m_n$. Applying the resolution yields us a clause which we add to the KB. We keep doing this until:\n", |
1172 | 1186 | "\n",
|
1173 | 1187 | "* There are no new clauses that can be added, in which case $\\text{KB} \\nvDash \\alpha$.\n",
|
1174 | 1188 | "* Two clauses resolve to yield the <em>empty clause</em>, in which case $\\text{KB} \\vDash \\alpha$.\n",
|
|
2009 | 2023 | "metadata": {},
|
2010 | 2024 | "source": [
|
2011 | 2025 | "### Forward and backward chaining\n",
|
2012 |
| - "Previously, we said we will look at two algorithms to check if a sentence is entailed by the `KB`, \n", |
2013 |
| - "but here's a third one. \n", |
| 2026 | + "Previously, we said we will look at two algorithms to check if a sentence is entailed by the `KB`. Here's a third one. \n", |
2014 | 2027 | "The difference here is that our goal now is to determine if a knowledge base of definite clauses entails a single proposition symbol *q* - the query.\n",
|
2015 |
| - "There is a catch however, the knowledge base can only contain **Horn clauses**.\n", |
| 2028 | + "There is a catch however - the knowledge base can only contain **Horn clauses**.\n", |
2016 | 2029 | "<br>\n",
|
2017 | 2030 | "#### Horn Clauses\n",
|
2018 | 2031 | "Horn clauses can be defined as a *disjunction* of *literals* with **at most** one positive literal. \n",
|
|
2346 | 2359 | {
|
2347 | 2360 | "cell_type": "code",
|
2348 | 2361 | "execution_count": 41,
|
2349 |
| - "metadata": {}, |
| 2362 | + "metadata": { |
| 2363 | + "collapsed": true |
| 2364 | + }, |
2350 | 2365 | "outputs": [],
|
2351 | 2366 | "source": [
|
2352 | 2367 | "clauses = ['(B & F)==>E', \n",
|
|
2370 | 2385 | {
|
2371 | 2386 | "cell_type": "code",
|
2372 | 2387 | "execution_count": 42,
|
2373 |
| - "metadata": {}, |
| 2388 | + "metadata": { |
| 2389 | + "collapsed": true |
| 2390 | + }, |
2374 | 2391 | "outputs": [],
|
2375 | 2392 | "source": [
|
2376 | 2393 | "definite_clauses_KB = PropDefiniteKB()\n",
|
|
2800 | 2817 | {
|
2801 | 2818 | "cell_type": "code",
|
2802 | 2819 | "execution_count": 49,
|
2803 |
| - "metadata": {}, |
| 2820 | + "metadata": { |
| 2821 | + "collapsed": true |
| 2822 | + }, |
2804 | 2823 | "outputs": [],
|
2805 | 2824 | "source": [
|
2806 | 2825 | "A, B, C, D = expr('A, B, C, D')"
|
|
2932 | 2951 | "This is similar to finding a neighboring state in the `hill_climbing` algorithm.\n",
|
2933 | 2952 | "<br>\n",
|
2934 | 2953 | "The symbol to be flipped is decided by an evaluation function that counts the number of unsatisfied clauses.\n",
|
2935 |
| - "Sometimes, symbols are also flipped randomly, to avoid local optima. A subtle balance between greediness and randomness is required. Alternatively, some versions of the algorithm restart with a completely new random assignment if no solution has been found for too long, as a way of getting out of local minima of numbers of unsatisfied clauses.\n", |
| 2954 | + "Sometimes, symbols are also flipped randomly to avoid local optima. A subtle balance between greediness and randomness is required. Alternatively, some versions of the algorithm restart with a completely new random assignment if no solution has been found for too long as a way of getting out of local minima of numbers of unsatisfied clauses.\n", |
2936 | 2955 | "<br>\n",
|
2937 | 2956 | "<br>\n",
|
2938 | 2957 | "Let's have a look at the algorithm."
|
|
3097 | 3116 | {
|
3098 | 3117 | "cell_type": "code",
|
3099 | 3118 | "execution_count": 56,
|
3100 |
| - "metadata": {}, |
| 3119 | + "metadata": { |
| 3120 | + "collapsed": true |
| 3121 | + }, |
3101 | 3122 | "outputs": [],
|
3102 | 3123 | "source": [
|
3103 | 3124 | "A, B, C, D = expr('A, B, C, D')"
|
|
3173 | 3194 | {
|
3174 | 3195 | "cell_type": "code",
|
3175 | 3196 | "execution_count": 60,
|
3176 |
| - "metadata": {}, |
| 3197 | + "metadata": { |
| 3198 | + "collapsed": true |
| 3199 | + }, |
3177 | 3200 | "outputs": [],
|
3178 | 3201 | "source": [
|
3179 | 3202 | "WalkSAT([A & B, C | D, ~(D | B)], 0.5, 1000)"
|
|
3198 | 3221 | {
|
3199 | 3222 | "cell_type": "code",
|
3200 | 3223 | "execution_count": 61,
|
3201 |
| - "metadata": {}, |
| 3224 | + "metadata": { |
| 3225 | + "collapsed": true |
| 3226 | + }, |
3202 | 3227 | "outputs": [],
|
3203 | 3228 | "source": [
|
3204 | 3229 | "def WalkSAT_CNF(sentence, p=0.5, max_flips=10000):\n",
|
|
3248 | 3273 | {
|
3249 | 3274 | "cell_type": "code",
|
3250 | 3275 | "execution_count": 63,
|
3251 |
| - "metadata": {}, |
| 3276 | + "metadata": { |
| 3277 | + "collapsed": true |
| 3278 | + }, |
3252 | 3279 | "outputs": [],
|
3253 | 3280 | "source": [
|
3254 | 3281 | "sentence_1 = A |'<=>'| B\n",
|
|
3602 | 3629 | {
|
3603 | 3630 | "cell_type": "code",
|
3604 | 3631 | "execution_count": 69,
|
3605 |
| - "metadata": {}, |
| 3632 | + "metadata": { |
| 3633 | + "collapsed": true |
| 3634 | + }, |
3606 | 3635 | "outputs": [],
|
3607 | 3636 | "source": [
|
3608 | 3637 | "clauses = []"
|
|
3629 | 3658 | {
|
3630 | 3659 | "cell_type": "code",
|
3631 | 3660 | "execution_count": 70,
|
3632 |
| - "metadata": {}, |
| 3661 | + "metadata": { |
| 3662 | + "collapsed": true |
| 3663 | + }, |
3633 | 3664 | "outputs": [],
|
3634 | 3665 | "source": [
|
3635 | 3666 | "clauses.append(expr(\"(American(x) & Weapon(y) & Sells(x, y, z) & Hostile(z)) ==> Criminal(x)\"))"
|
|
3648 | 3679 | {
|
3649 | 3680 | "cell_type": "code",
|
3650 | 3681 | "execution_count": 71,
|
3651 |
| - "metadata": {}, |
| 3682 | + "metadata": { |
| 3683 | + "collapsed": true |
| 3684 | + }, |
3652 | 3685 | "outputs": [],
|
3653 | 3686 | "source": [
|
3654 | 3687 | "clauses.append(expr(\"Enemy(Nono, America)\"))"
|
|
3667 | 3700 | {
|
3668 | 3701 | "cell_type": "code",
|
3669 | 3702 | "execution_count": 72,
|
3670 |
| - "metadata": {}, |
| 3703 | + "metadata": { |
| 3704 | + "collapsed": true |
| 3705 | + }, |
3671 | 3706 | "outputs": [],
|
3672 | 3707 | "source": [
|
3673 | 3708 | "clauses.append(expr(\"Owns(Nono, M1)\"))\n",
|
|
3689 | 3724 | {
|
3690 | 3725 | "cell_type": "code",
|
3691 | 3726 | "execution_count": 73,
|
3692 |
| - "metadata": {}, |
| 3727 | + "metadata": { |
| 3728 | + "collapsed": true |
| 3729 | + }, |
3693 | 3730 | "outputs": [],
|
3694 | 3731 | "source": [
|
3695 | 3732 | "clauses.append(expr(\"(Missile(x) & Owns(Nono, x)) ==> Sells(West, x, Nono)\"))"
|
|
3708 | 3745 | {
|
3709 | 3746 | "cell_type": "code",
|
3710 | 3747 | "execution_count": 74,
|
3711 |
| - "metadata": {}, |
| 3748 | + "metadata": { |
| 3749 | + "collapsed": true |
| 3750 | + }, |
3712 | 3751 | "outputs": [],
|
3713 | 3752 | "source": [
|
3714 | 3753 | "clauses.append(expr(\"American(West)\"))"
|
|
3726 | 3765 | {
|
3727 | 3766 | "cell_type": "code",
|
3728 | 3767 | "execution_count": 75,
|
3729 |
| - "metadata": {}, |
| 3768 | + "metadata": { |
| 3769 | + "collapsed": true |
| 3770 | + }, |
3730 | 3771 | "outputs": [],
|
3731 | 3772 | "source": [
|
3732 | 3773 | "clauses.append(expr(\"Missile(x) ==> Weapon(x)\"))\n",
|
|
3743 | 3784 | {
|
3744 | 3785 | "cell_type": "code",
|
3745 | 3786 | "execution_count": 76,
|
3746 |
| - "metadata": {}, |
| 3787 | + "metadata": { |
| 3788 | + "collapsed": true |
| 3789 | + }, |
3747 | 3790 | "outputs": [],
|
3748 | 3791 | "source": [
|
3749 | 3792 | "crime_kb = FolKB(clauses)"
|
|
4039 | 4082 | "metadata": {},
|
4040 | 4083 | "source": [
|
4041 | 4084 | "### Forward Chaining Algorithm\n",
|
4042 |
| - "We consider the simple forward-chaining algorithm presented in <em>Figure 9.3</em>. We look at each rule in the knoweldge base and see if the premises can be satisfied. This is done by finding a substitution which unifies each of the premise with a clause in the `KB`. If we are able to unify the premises, the conclusion (with the corresponding substitution) is added to the `KB`. This inferencing process is repeated until either the query can be answered or till no new sentences can be added. We test if the newly added clause unifies with the query in which case the substitution yielded by `unify` is an answer to the query. If we run out of sentences to infer, this means the query was a failure.\n", |
| 4085 | + "We consider the simple forward-chaining algorithm presented in <em>Figure 9.3</em>. We look at each rule in the knowledge base and see if the premises can be satisfied. This is done by finding a substitution which unifies each of the premise with a clause in the `KB`. If we are able to unify the premises, the conclusion (with the corresponding substitution) is added to the `KB`. This inferencing process is repeated until either the query can be answered or till no new sentences can be added. We test if the newly added clause unifies with the query in which case the substitution yielded by `unify` is an answer to the query. If we run out of sentences to infer, this means the query was a failure.\n", |
4043 | 4086 | "\n",
|
4044 | 4087 | "The function `fol_fc_ask` is a generator which yields all substitutions which validate the query."
|
4045 | 4088 | ]
|
|
4514 | 4557 | {
|
4515 | 4558 | "cell_type": "code",
|
4516 | 4559 | "execution_count": 89,
|
4517 |
| - "metadata": {}, |
| 4560 | + "metadata": { |
| 4561 | + "collapsed": true |
| 4562 | + }, |
4518 | 4563 | "outputs": [],
|
4519 | 4564 | "source": [
|
4520 | 4565 | "# Rebuild KB because running fol_fc_ask would add new facts to the KB\n",
|
|
4951 | 4996 | "name": "python",
|
4952 | 4997 | "nbconvert_exporter": "python",
|
4953 | 4998 | "pygments_lexer": "ipython3",
|
4954 |
| - "version": "3.6.4" |
| 4999 | + "version": "3.6.1" |
4955 | 5000 | }
|
4956 | 5001 | },
|
4957 | 5002 | "nbformat": 4,
|
|
0 commit comments