sine-patre/fol/kb.py

169 lines
4.0 KiB
Python

import fol
class Kb:
def __init__(self):
self.base = []
self.counter = fol.cnf.Counter()
def make_f(self, h):
self.counter.begin()
f = h
if type(f) is str:
f = fol.p(f)
self.counter.begin()
f = fol.cnf.cnf(f, self.counter)
if f.name == 'OR':
f = f.list_of('OR')
else:
f = [f]
return f
def make_req(self, h):
f = h
if type(f) is str:
f = fol.p(f)
if f.name == 'OR':
f = f.list_of('OR')
else:
f = [f]
return f
def check_request(self, request):
for r in request:
if r.name not in ['NOT', 'PRED']:
raise Exception(f'invalid statement {repr(r)}')
def conds(self, statement):
result = []
for f in statement:
if f.name == 'NOT':
result.append(f.children[0])
return result
def conclusion(self, statement):
for f in statement:
if f.name != 'NOT':
return f
return None
@property
def rules(self):
res = []
for r in self.base:
if len(r) > 1:
res.append(r)
return res
@property
def facts(self):
res = []
for r in self.base:
if len(r) == 1:
res.append(r[0])
return res
def exists(self, f):
h = self.make_f(f)
for g in self.base:
if self.clause_equals(h, g):
return True
return False
def clause_equals(self, left, right):
if len(left) != len(right):
return False
for element in left:
if not self.clause_in(element, right):
return False
for element in right:
if not self.clause_in(element, left):
return False
return True
def clause_in(self, clause, lst):
for other in lst:
same = fol.unify(clause, other) is not None
if same:
return True
return False
def tell(self, request):
req = self.make_f(request)
self.check_request(req)
self.base.append(req)
def ask(self, request):
for i in range(20):
n = len(self.base)
self.update()
if n == len(self.base):
break
req = self.make_req(request)
results = []
for f in self.base:
s = fol.unify(f, req)
if s is not None:
results.append(s)
return results
def update(self):
for rule in self.rules:
conclusion = self.conclusion(rule)
solution = []
solutions = []
self.solve(rule, self.conds(rule), self.facts, solution, solutions)
for sol in solutions:
concl = conclusion.subst(sol)
if not self.exists(concl):
self.base.append([concl])
return
def merge_substs(self, substs):
res = {}
for s in substs:
for k in s.keys():
if k in res.keys() and not res[k].equals(s[k]):
return None
res[k] = s[k]
return res
def solve(self, rule, conds, facts, solution, solutions):
if len(conds) == 0:
substs = []
for sol in solution:
s = fol.unify(sol[0], sol[1])
if s is not None:
substs.append(s)
s = self.merge_substs(substs)
if s is not None:
solutions.append(s)
return
for cond in conds:
for fact in facts:
solution.append((cond, fact))
self.solve(
rule,
[c for c in conds if c != cond],
[f for f in facts if f != fact],
solution,
solutions
)
solution.pop()