Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c3a6fbc

Browse files
committed
Update logic.py to 3rd edition.
1 parent 1c9a371 commit c3a6fbc

File tree

1 file changed

+67
-113
lines changed

1 file changed

+67
-113
lines changed

logic.py

Lines changed: 67 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Representations and Inference for Logic (Chapters 7-10)
1+
"""Representations and Inference for Logic (Chapters 7-9, 12)
22
33
Covers both Propositional and First-Order Logic. First we have four
44
important data types:
@@ -23,12 +23,8 @@
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
3228
import agents
3329
from utils import *
3430

@@ -410,7 +406,7 @@ def pl_true(exp, model={}):
410406

411407
def 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

609603
def 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]
631625
Fig[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()
635629
for 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

642635
def 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

725717
def 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+
930911
def 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

Comments
 (0)