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

Skip to content

Commit b14e21a

Browse files
Chipe1norvig
authored andcommitted
Implemented fol_fc_ask() (aimacode#535)
1 parent 4c873c8 commit b14e21a

File tree

3 files changed

+94
-45
lines changed

3 files changed

+94
-45
lines changed

aima-data

logic.py

Lines changed: 72 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,16 @@ def prop_symbols(x):
224224
return list(set(symbol for arg in x.args for symbol in prop_symbols(arg)))
225225

226226

227+
def constant_symbols(x):
228+
"""Return a list of all constant symbols in x."""
229+
if not isinstance(x, Expr):
230+
return []
231+
elif is_prop_symbol(x.op) and not x.args:
232+
return [x]
233+
else:
234+
return list({symbol for arg in x.args for symbol in constant_symbols(arg)})
235+
236+
227237
def tt_true(s):
228238
"""Is a propositional sentence a tautology?
229239
>>> tt_true('P | ~P')
@@ -845,26 +855,6 @@ def subst(s, x):
845855
return Expr(x.op, *[subst(s, arg) for arg in x.args])
846856

847857

848-
def fol_fc_ask(KB, alpha):
849-
"""A simple forward-chaining algorithm. [Figure 9.3]"""
850-
new = []
851-
while new is not None:
852-
for rule in KB.clauses:
853-
p, q = parse_definite_clause(standardize_variables(rule))
854-
for p_ in KB.clauses:
855-
if p != p_:
856-
for theta in KB.clauses:
857-
if subst(theta, p) == subst(theta, p_):
858-
q_ = subst(theta, q)
859-
if not unify(q_, KB.sentence in KB) or not unify(q_, new):
860-
new.append(q_)
861-
phi = unify(q_, alpha)
862-
if phi is not None:
863-
return phi
864-
KB.tell(new)
865-
return None
866-
867-
868858
def standardize_variables(sentence, dic=None):
869859
"""Replace all the variables in sentence with new variables."""
870860
if dic is None:
@@ -921,31 +911,42 @@ def fetch_rules_for_goal(self, goal):
921911
return self.clauses
922912

923913

924-
test_kb = FolKB(
925-
map(expr, ['Farmer(Mac)',
926-
'Rabbit(Pete)',
927-
'Mother(MrsMac, Mac)',
928-
'Mother(MrsRabbit, Pete)',
929-
'(Rabbit(r) & Farmer(f)) ==> Hates(f, r)',
930-
'(Mother(m, c)) ==> Loves(m, c)',
931-
'(Mother(m, r) & Rabbit(r)) ==> Rabbit(m)',
932-
'(Farmer(f)) ==> Human(f)',
933-
# Note that this order of conjuncts
934-
# would result in infinite recursion:
935-
# '(Human(h) & Mother(m, h)) ==> Human(m)'
936-
'(Mother(m, h) & Human(h)) ==> Human(m)'
937-
]))
914+
def fol_fc_ask(KB, alpha):
915+
"""A simple forward-chaining algorithm. [Figure 9.3]"""
916+
# TODO: Improve efficiency
917+
def enum_subst(KB):
918+
kb_vars = list({v for clause in KB.clauses for v in variables(clause)})
919+
kb_consts = list({c for clause in KB.clauses for c in constant_symbols(clause)})
920+
for assignment_list in itertools.product(kb_consts, repeat=len(kb_vars)):
921+
theta = {x: y for x, y in zip(kb_vars, assignment_list)}
922+
yield theta
923+
924+
# check if we can answer without new inferences
925+
for q in KB.clauses:
926+
phi = unify(q, alpha, {})
927+
if phi is not None:
928+
yield phi
938929

939-
crime_kb = FolKB(
940-
map(expr, ['(American(x) & Weapon(y) & Sells(x, y, z) & Hostile(z)) ==> Criminal(x)',
941-
'Owns(Nono, M1)',
942-
'Missile(M1)',
943-
'(Missile(x) & Owns(Nono, x)) ==> Sells(West, x, Nono)',
944-
'Missile(x) ==> Weapon(x)',
945-
'Enemy(x, America) ==> Hostile(x)',
946-
'American(West)',
947-
'Enemy(Nono, America)'
948-
]))
930+
while True:
931+
new = []
932+
for rule in KB.clauses:
933+
p, q = parse_definite_clause(rule)
934+
for theta in enum_subst(KB):
935+
if any([set(subst(theta, p)) == set(subst(theta, p_))
936+
for p_ in itertools.combinations(KB.clauses, len(p))]):
937+
q_ = subst(theta, q)
938+
if all([unify(x, q_, {}) is None for x in KB.clauses + new]):
939+
print('Added', q_)
940+
new.append(q_)
941+
phi = unify(q_, alpha, {})
942+
if phi is not None:
943+
print(q_, alpha)
944+
yield phi
945+
if not new:
946+
break
947+
for clause in new:
948+
KB.tell(clause)
949+
return None
949950

950951

951952
def fol_bc_ask(KB, query):
@@ -972,6 +973,33 @@ def fol_bc_and(KB, goals, theta):
972973
for theta2 in fol_bc_and(KB, rest, theta1):
973974
yield theta2
974975

976+
977+
test_kb = FolKB(
978+
map(expr, ['Farmer(Mac)',
979+
'Rabbit(Pete)',
980+
'Mother(MrsMac, Mac)',
981+
'Mother(MrsRabbit, Pete)',
982+
'(Rabbit(r) & Farmer(f)) ==> Hates(f, r)',
983+
'(Mother(m, c)) ==> Loves(m, c)',
984+
'(Mother(m, r) & Rabbit(r)) ==> Rabbit(m)',
985+
'(Farmer(f)) ==> Human(f)',
986+
# Note that this order of conjuncts
987+
# would result in infinite recursion:
988+
# '(Human(h) & Mother(m, h)) ==> Human(m)'
989+
'(Mother(m, h) & Human(h)) ==> Human(m)'
990+
]))
991+
992+
crime_kb = FolKB(
993+
map(expr, ['(American(x) & Weapon(y) & Sells(x, y, z) & Hostile(z)) ==> Criminal(x)',
994+
'Owns(Nono, M1)',
995+
'Missile(M1)',
996+
'(Missile(x) & Owns(Nono, x)) ==> Sells(West, x, Nono)',
997+
'Missile(x) ==> Weapon(x)',
998+
'Enemy(x, America) ==> Hostile(x)',
999+
'American(West)',
1000+
'Enemy(Nono, America)'
1001+
]))
1002+
9751003
# ______________________________________________________________________________
9761004

9771005
# Example application (not in the book).

tests/test_logic.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,11 @@ def test_prop_symbols():
190190
assert set(prop_symbols(expr('(x & B(z)) ==> Farmer(y) | A'))) == {A, expr('Farmer(y)'), expr('B(z)')}
191191

192192

193+
def test_constant_symbols():
194+
assert set(constant_symbols(expr('x & y & z | A'))) == {A}
195+
assert set(constant_symbols(expr('(x & B(z)) & Father(John) ==> Farmer(y) | A'))) == {A, expr('John')}
196+
197+
193198
def test_eliminate_implications():
194199
assert repr(eliminate_implications('A ==> (~B <== C)')) == '((~B | ~C) | ~A)'
195200
assert repr(eliminate_implications(A ^ B)) == '((A & ~B) | (~A & B))'
@@ -258,6 +263,22 @@ def test_ask(query, kb=None):
258263
assert repr(test_ask('Criminal(x)', crime_kb)) == '[{x: West}]'
259264

260265

266+
def test_fol_fc_ask():
267+
def test_ask(query, kb=None):
268+
q = expr(query)
269+
test_variables = variables(q)
270+
answers = fol_fc_ask(kb or test_kb, q)
271+
print(answers)
272+
return sorted(
273+
[dict((x, v) for x, v in list(a.items()) if x in test_variables)
274+
for a in answers], key=repr)
275+
## Take too long to run
276+
#assert repr(test_ask('Farmer(x)')) == '[{x: Mac}]'
277+
#assert repr(test_ask('Human(x)')) == '[{x: Mac}, {x: MrsMac}]'
278+
#assert repr(test_ask('Rabbit(x)')) == '[{x: MrsRabbit}, {x: Pete}]'
279+
#assert repr(test_ask('Criminal(x)', crime_kb)) == '[{x: West}]'
280+
281+
261282
def test_d():
262283
assert d(x * x - x, x) == 2 * x - 1
263284

0 commit comments

Comments
 (0)