1- """Representations and Inference for Logic (Chapters 7-10 ) 
1+ """Representations and Inference for Logic (Chapters 7-9, 12 ) 
22
33Covers both Propositional and First-Order Logic. First we have four 
44important data types: 
2323    unify            Do unification of two FOL sentences 
2424    diff, simp       Symbolic differentiation and simplification 
2525""" 
26- # (Written for the second edition of AIMA; expect some discrepanciecs 
27- # from the third edition until this gets reviewed.) 
2826
29- from  __future__ import  generators 
30- import  re 
31- import  itertools 
27+ import  itertools , re 
3228import  agents 
3329from  utils  import  * 
3430
@@ -410,7 +406,7 @@ def pl_true(exp, model={}):
410406
411407def  to_cnf (s ):
412408    """Convert a propositional logical sentence s to conjunctive normal form. 
413-     That is, to the form ((A | ~B | ...) & (B | C | ...) & ...) [p. 215 ] 
409+     That is, to the form ((A | ~B | ...) & (B | C | ...) & ...) [p. 253 ] 
414410    >>> to_cnf("~(B|C)") 
415411    (~B & ~C) 
416412    >>> to_cnf("B <=> (P1|P2)") 
@@ -423,7 +419,7 @@ def to_cnf(s):
423419    ((D | A | B | C) & (E | A | B | C)) 
424420    """ 
425421    if  isinstance (s , str ): s  =  expr (s )
426-     s  =  eliminate_implications (s ) # Steps 1, 2 from p. 215  
422+     s  =  eliminate_implications (s ) # Steps 1, 2 from p. 253  
427423    s  =  move_not_inwards (s ) # Step 3 
428424    return  distribute_and_over_or (s ) # Step 4 
429425
@@ -582,13 +578,11 @@ def pl_resolve(ci, cj):
582578
583579#______________________________________________________________________________ 
584580
585- class  PropHornKB (PropKB ):
586-     "A KB of propositional Horn clauses." 
587-     # Actually definite clauses, but I won't resolve this discrepancy till 
588-     # the code upgrade to the 3rd edition. 
581+ class  PropDefiniteKB (PropKB ):
582+     "A KB of propositional definite clauses." 
589583
590584    def  tell (self , sentence ):
591-         "Add a Horn  clause to this KB." 
585+         "Add a definite  clause to this KB." 
592586        assert  is_definite_clause (sentence ), "Must be definite clause" 
593587        self .clauses .append (sentence )
594588
@@ -607,37 +601,36 @@ def clauses_with_premise(self, p):
607601                if  c .op  ==  '>>'  and  p  in  conjuncts (c .args [0 ])]
608602
609603def  pl_fc_entails (KB , q ):
610-     """Use forward chaining to see if a HornKB entails symbol q. [Fig. 7.14] 
604+     """Use forward chaining to see if a PropDefiniteKB entails symbol q. 
605+     [Fig. 7.15] 
611606    >>> pl_fc_entails(Fig[7,15], expr('Q')) 
612607    True 
613608    """ 
614609    count  =  dict ([(c , len (conjuncts (c .args [0 ]))) for  c  in  KB .clauses 
615610                                                 if  c .op  ==  '>>' ])
616611    inferred  =  DefaultDict (False )
617612    agenda  =  [s  for  s  in  KB .clauses  if  is_prop_symbol (s .op )]
618-     if  q  in  agenda : return  True 
619613    while  agenda :
620614        p  =  agenda .pop ()
615+         if  p  ==  q : return  True 
621616        if  not  inferred [p ]:
622617            inferred [p ] =  True 
623618            for  c  in  KB .clauses_with_premise (p ):
624619                count [c ] -=  1 
625620                if  count [c ] ==  0 :
626-                     if  c .args [1 ] ==  q : return  True 
627621                    agenda .append (c .args [1 ])
628622    return  False 
629623
630624## Wumpus World example [Fig. 7.13] 
631625Fig [7 ,13 ] =  expr ("(B11 <=> (P12 | P21))  &  ~B11" )
632626
633- ## Propositional Logic Forward Chaining example [Fig. 7.15 ] 
634- Fig [7 ,15 ] =  PropHornKB ()
627+ ## Propositional Logic Forward Chaining example [Fig. 7.16 ] 
628+ Fig [7 ,15 ] =  PropDefiniteKB ()
635629for  s  in  "P>>Q   (L&M)>>P   (B&L)>>M   (A&P)>>L   (A&B)>>L   A   B" .split ():
636630    Fig [7 ,15 ].tell (expr (s ))
637631
638632#______________________________________________________________________________ 
639- 
640- # DPLL-Satisfiable [Fig. 7.16] 
633+ # DPLL-Satisfiable [Fig. 7.17] 
641634
642635def  dpll_satisfiable (s ):
643636    """Check satisfiability of a propositional sentence. 
@@ -718,9 +711,8 @@ def literal_symbol(literal):
718711    else :
719712        return  literal 
720713
721- 
722714#______________________________________________________________________________ 
723- # Walk-SAT [Fig. 7.17 ] 
715+ # Walk-SAT [Fig. 7.18 ] 
724716
725717def  WalkSAT (clauses , p = 0.5 , max_flips = 10000 ):
726718    ## model is a random assignment of true/false to the symbols in clauses 
@@ -742,45 +734,31 @@ def WalkSAT(clauses, p=0.5, max_flips=10000):
742734        model [sym ] =  not  model [sym ]
743735
744736#______________________________________________________________________________ 
745- # PL-Wumpus-Agent [Fig. 7.19] 
746737
747- class  PLWumpusAgent (agents .Agent ):
738+ class  HybridWumpusAgent (agents .Agent ):
748739    "An agent for the wumpus world that does logical inference. [Fig. 7.19]" "" 
749740    def  __init__ (self ):
750-         KB  =  FolKB () ## shouldn't this be a propositional KB? *** 
751-         x , y , orientation  =  1 , 1 , (1 , 0 )
752-         visited  =  set () ## squares already visited 
753-         plan  =  []
754- 
755-         def  program (percept ):
756-             stench , breeze , glitter  =  percept 
757-             x , y , orientation  =  \
758-                 update_position (x , y , orientation , program .action )
759-             KB .tell ('%sS_%d,%d'  %  (if_ (stench , '' , '~' ), x , y ))
760-             KB .tell ('%sB_%d,%d'  %  (if_ (breeze , '' , '~' ), x , y ))
761-             if  glitter : program .action  =  'Grab' 
762-             elif  plan : program .action  =  plan .pop ()
763-             else :
764-                 for  [i , j ] in  fringe (visited ):
765-                     if  KB .ask ('~P_%d,%d & ~W_%d,%d'  %  (i , j , i , j )) !=  False :
766-                         raise  NotImplementedError 
767-                     KB .ask ('~P_%d,%d | ~W_%d,%d'  %  (i , j , i , j )) !=  False 
768-             if  program .action  is  None :
769-                 program .action  =  random .choice (['Forward' , 'Right' , 'Left' ])
770-             return  program .action 
771- 
772-         program .action  =  None 
773- 
774-         agents .Agent .__init__ (self , program )
775- 
776- def  update_position (x , y , orientation , action ):
777-     if  action  ==  'TurnRight' :
778-         orientation  =  turn_right (orientation )
779-     elif  action  ==  'TurnLeft' :
780-         orientation  =  turn_left (orientation )
781-     elif  action  ==  'Forward' :
782-         x , y  =  x  +  vector_add ((x , y ), orientation )
783-     return  x , y , orientation 
741+         unimplemented ()
742+ 
743+ def  plan_route (current , goals , allowed ):
744+     unimplemented ()
745+ 
746+ #______________________________________________________________________________ 
747+ 
748+ def  SAT_plan (init , transition , goal , t_max , SAT_solver = dpll_satisfiable ):
749+     "[Fig. 7.22]" 
750+     for  t  in  range (t_max ):
751+         cnf  =  translate_to_SAT (init , transition , goal , t )
752+         model  =  SAT_solver (cnf )
753+         if  model  is  not False :
754+             return  extract_solution (model )
755+     return  None 
756+ 
757+ def  translate_to_SAT (init , transition , goal , t ):
758+     unimplemented ()
759+ 
760+ def  extract_solution (model ):
761+     unimplemented ()
784762
785763#______________________________________________________________________________ 
786764
@@ -868,17 +846,17 @@ def fol_fc_ask(KB, alpha):
868846    while  True :
869847        new  =  {}
870848        for  r  in  KB .clauses :
871-             ps , q  =  parse_definite_clause (standardize_apart (r ))
849+             ps , q  =  parse_definite_clause (standardize_variables (r ))
872850            raise  NotImplementedError 
873851
874- def  standardize_apart (sentence , dic = None ):
852+ def  standardize_variables (sentence , dic = None ):
875853    """Replace all the variables in sentence with new variables. 
876854    >>> e = expr('F(a, b, c) & G(c, A, 23)') 
877-     >>> len(variables(standardize_apart (e))) 
855+     >>> len(variables(standardize_variables (e))) 
878856    3 
879-     >>> variables(e).intersection(variables(standardize_apart (e))) 
857+     >>> variables(e).intersection(variables(standardize_variables (e))) 
880858    set([]) 
881-     >>> is_variable(standardize_apart (expr('x'))) 
859+     >>> is_variable(standardize_variables (expr('x'))) 
882860    True 
883861    """ 
884862    if  dic  is  None : dic  =  {}
@@ -888,14 +866,14 @@ def standardize_apart(sentence, dic=None):
888866        if  sentence  in  dic :
889867            return  dic [sentence ]
890868        else :
891-             v  =  Expr ('v_%d'  %  standardize_apart .counter .next ())
869+             v  =  Expr ('v_%d'  %  standardize_variables .counter .next ())
892870            dic [sentence ] =  v 
893871            return  v 
894872    else :
895873        return  Expr (sentence .op ,
896-                     * [standardize_apart (a , dic ) for  a  in  sentence .args ])
874+                     * [standardize_variables (a , dic ) for  a  in  sentence .args ])
897875
898- standardize_apart .counter  =  itertools .count ()
876+ standardize_variables .counter  =  itertools .count ()
899877
900878#______________________________________________________________________________ 
901879
@@ -922,15 +900,18 @@ def tell(self, sentence):
922900            raise  Exception ("Not a definite clause: %s"  %  sentence )
923901
924902    def  ask_generator (self , query ):
925-         return  fol_bc_ask (self , [ query ] )
903+         return  fol_bc_ask (self , query )
926904
927905    def  retract (self , sentence ):
928906        self .clauses .remove (sentence )
929907
908+     def  fetch_rules_for_goal (self , goal ):
909+         return  self .clauses 
910+ 
930911def  test_ask (query , kb = None ):
931912    q  =  expr (query )
932913    vars  =  variables (q )
933-     answers  =  fol_bc_ask (kb  or  test_kb , [ q ] )
914+     answers  =  fol_bc_ask (kb  or  test_kb , q )
934915    return  sorted ([pretty (dict ((x , v ) for  x , v  in  a .items () if  x  in  vars ))
935916                   for  a  in  answers ],
936917                  key = repr )
@@ -964,7 +945,7 @@ def test_ask(query, kb=None):
964945     ])
965946)
966947
967- def  fol_bc_ask (KB , goals ,  theta = {} ):
948+ def  fol_bc_ask (KB , query ):
968949    """A simple backward-chaining algorithm for first-order logic. [Fig. 9.6] 
969950    KB should be an instance of FolKB, and goals a list of literals. 
970951    >>> test_ask('Farmer(x)') 
@@ -980,51 +961,24 @@ def fol_bc_ask(KB, goals, theta={}):
980961    >>> test_ask('Criminal(x)', crime_kb) 
981962    ['{x: West}'] 
982963    """ 
983-     if  not  goals :
964+     return  fol_bc_or (KB , query , {})
965+ 
966+ def  fol_bc_or (KB , goal , theta ):
967+     for  rule  in  KB .fetch_rules_for_goal (goal ):
968+         lhs , rhs  =  parse_definite_clause (standardize_variables (rule ))
969+         for  theta1  in  fol_bc_and (KB , lhs , unify (rhs , goal , theta )):
970+             yield  theta1 
971+ 
972+ def  fol_bc_and (KB , goals , theta ):
973+     if  theta  is  None :
974+         pass 
975+     elif  not  goals :
984976        yield  theta 
985-         return 
986-     q1  =  subst (theta , goals [0 ])
987-     for  r  in  KB .clauses :
988-         ps , q  =  parse_definite_clause (standardize_apart (r ))
989-         theta1  =  unify (q , q1 , {})
990-         if  theta1  is  not None :
991-             new_goals  =  ps  +  goals [1 :]
992-             for  ans  in  fol_bc_ask (KB , new_goals , subst_compose (theta1 , theta )):
993-                 yield  ans 
994- 
995- def  subst_compose (s1 , s2 ):
996-     """Return the substitution which is equivalent to applying s2 to 
997-     the result of applying s1 to an expression. 
998- 
999-     >>> s1 = {x: A, y: B} 
1000-     >>> s2 = {z: x, x: C} 
1001-     >>> p = F(x) & G(y) & expr('H(z)') 
1002-     >>> subst(s1, p) 
1003-     ((F(A) & G(B)) & H(z)) 
1004-     >>> subst(s2, p) 
1005-     ((F(C) & G(y)) & H(x)) 
1006- 
1007-     >>> subst(s2, subst(s1, p)) 
1008-     ((F(A) & G(B)) & H(x)) 
1009-     >>> subst(subst_compose(s1, s2), p) 
1010-     ((F(A) & G(B)) & H(x)) 
1011- 
1012-     >>> subst(s1, subst(s2, p)) 
1013-     ((F(C) & G(B)) & H(A)) 
1014-     >>> subst(subst_compose(s2, s1), p) 
1015-     ((F(C) & G(B)) & H(A)) 
1016-     >>> ppsubst(subst_compose(s1, s2)) 
1017-     {x: A, y: B, z: x} 
1018-     >>> ppsubst(subst_compose(s2, s1)) 
1019-     {x: C, y: B, z: A} 
1020-     >>> subst(subst_compose(s1, s2), p) == subst(s2, subst(s1, p)) 
1021-     True 
1022-     >>> subst(subst_compose(s2, s1), p) == subst(s1, subst(s2, p)) 
1023-     True 
1024-     """ 
1025-     result  =  dict ((x , s2 .get (v , v )) for  x , v  in  s1 .items ())
1026-     result .update ((x , v ) for  x , v  in  s2 .items () if  x  not  in s1 )
1027-     return  result 
977+     else :
978+         first , rest  =  goals [0 ], goals [1 :]
979+         for  theta1  in  fol_bc_or (KB , subst (theta , first ), theta ):
980+             for  theta2  in  fol_bc_and (KB , rest , theta1 ):
981+                 yield  theta2 
1028982
1029983#______________________________________________________________________________ 
1030984
0 commit comments