;;; -*- Mode: Lisp; Syntax: Common-Lisp; -*- File: logic/horn.lisp ;;;; Logical Reasoning in Horn Clause Knowledge Bases (defstructure horn-kb ;; Index all Horn sentences by the predicate on the right-hand side. ;; That is, both (=> P (Q x)) and (Q 3) would be indexed under Q. (table (make-hash-table :test #'eq))) (defmethod tell ((kb horn-kb) sentence) "Add a sentence to a Horn knowledge base. Warn if its not a Horn sentence." (for each clause in (conjuncts (->horn sentence)) do ;; Each clause should be of form (=> P (Q x)); add to hash for Q (setf (gethash (op (arg2 clause)) (horn-kb-table kb)) (nconc (gethash (op (arg2 clause)) (horn-kb-table kb)) (list clause))))) (defmethod retract ((kb horn-kb) sentence) "Delete each conjunct of sentence from KB." (for each clause in (conjuncts (->horn sentence)) do ;; Each clause should be of form (=> P (Q x)); delete from hash for Q (deletef clause (gethash (op (arg2 clause)) (horn-kb-table kb)) :test #'renaming?))) (defmethod ask-each ((kb horn-kb) query fn) "Use backward chaining to decide if sentence is true." (back-chain-each kb (conjuncts (->cnf query)) +no-bindings+ fn)) (defun back-chain-each (kb goals bindings fn) "Solve the conjunction of goals by backward chaining. See [p 275], but notice that this implementation is different. It applies fn to each answer found, and handles composition differently." (cond ((eq bindings +fail+) +fail+) ((null goals) (funcall fn bindings)) (t (let ((goal (first goals))) (case (op goal) (FALSE +fail+) (TRUE (back-chain-each kb (rest goals) bindings fn)) (= (back-chain-each kb (rest goals) (unify (arg1 goal) (arg2 goal) bindings) fn)) (AND (back-chain-each kb (append (conjuncts goal) goals) bindings fn)) (OR (for each disjunct in (disjuncts goal) do (back-chain-each kb (cons disjunct goals) bindings fn))) (NOT +fail+) ; Horn clause provers can't handle NOT (t ;; Look at all the clauses that could conclude the goal. (for each clause in (gethash (op goal) (horn-kb-table kb)) do (let ((new-clause (rename-variables clause))) (back-chain-each kb (append (conjuncts (arg1 new-clause)) (rest goals)) (unify goal (arg2 new-clause) bindings) fn)))))))))