src/old-rule-trees.lisp @ 3cfc630a3e6e
Lots of cleanup, docstrings, comments
| author | Steve Losh <steve@stevelosh.com> | 
|---|---|
| date | Thu, 24 Nov 2016 16:08:20 +0000 | 
| parents | addb56e3eb9d | 
| children | (none) | 
;;;; Rule Trees --------------------------------------------------------------- (defun rule-head (rule) (first rule)) (defun rule-body (rule) (rest rule)) (defun rule-first-body (rule) (first (rule-body rule))) (defun rule-empty-p (rule) (null (rule-body rule))) (defun negationp (term) (and (consp term) (eql 'ggp-rules::not (first term)))) (defun bare-term (term) (if (negationp term) (second term) term)) (defun term< (t1 t2) (< (bare-term t1) (bare-term t2))) (defun sort-body (rule) (destructuring-bind (head . body) rule (list* head (sort body #'term<)))) (defun drop-first (rule) (destructuring-bind (head . body) rule (list* head (rest body)))) (defun find-smallest-body-term (rules) (-<> rules (mapcar #'rule-first-body <>) (sort <> #'term<) (first <>))) (defun partition-rules (rules) (let ((element (bare-term (find-smallest-body-term rules)))) (labels ((rule-requires (rule) (equal (rule-first-body rule) element)) (rule-disallows (rule) (equal (rule-first-body rule) `(ggp-rules::not ,element))) (rule-ignores (rule) (not (or (rule-requires rule) (rule-disallows rule))))) (values element (remove-if-not #'rule-disallows rules) (remove-if-not #'rule-requires rules) (remove-if-not #'rule-ignores rules))))) (defmethod print-object ((set hash-set) stream) (print-unreadable-object (set stream :type t :identity nil) (prin1 (set->list set) stream))) (defun hash-set= (s1 s2) (zerop (set-size (set-symmetric-diff s1 s2)))) (defun rule-head-in (set rule) (set-lookup set (rule-head rule))) (defun collapse-positive-heads (rules-and-heads) (destructuring-bind (rules heads) rules-and-heads (flet ((update-rule (rule) (cons (rule-head rule) (remove-if (curry #'set-lookup heads) (rule-body rule))))) (let* ((new-rules (set-map #'update-rule rules)) (new-heads (-<> new-rules (set-filter #'rule-empty-p <>) (set-map #'rule-head <>)))) (list (set-filter (complement (curry #'rule-head-in new-heads)) new-rules) (set-union heads new-heads)))))) (defun find-strictly-negative-rules (rules) (set-filter (lambda (rule) (every #'negationp (rule-body rule))) rules)) (defun collapse-negative-heads (rules-and-heads) (destructuring-bind (rules heads) rules-and-heads (if (zerop (set-size rules)) (list rules heads) (labels ((negation-satisfied-p (negation) (not (set-lookup heads (bare-term negation)))) (rule-satisfied-p (rule) (every #'negation-satisfied-p (rule-body rule))) (smallest-head () (-<> (set->list rules) (mapcar #'rule-head <>) (sort <> #'term<) (first <>))) (rules-with-head (head) (set-filter (lambda (rule) (eql head (rule-head rule))) rules))) (let* ((next (smallest-head)) (candidates (rules-with-head next))) (list (set-diff rules candidates) (if (some #'rule-satisfied-p (set->list candidates)) (set-insert heads next) heads))))))) (defun make-rule-tree (rules) "Create a rule tree ZDD from the given logical `rules`. `rules` should be a list of one layer-worth of rules, each of the form: `(head-term &rest body-terms)` Each head term should be a single variable. Each body term should be either a single variable or `(not variable)`. Rules and bodies do not need to be sorted beforehand. " (recursively ((rules (mapcar #'sort-body rules)) (accumulated-heads nil)) (let* ((heads (-<> rules (remove-if-not #'rule-empty-p <>) (mapcar #'rule-head <>) (remove-duplicates <> :test #'=) (union accumulated-heads <> :test #'=))) ; slow (next-rules (remove-if (lambda (rule) (member (rule-head rule) heads :test #'equal)) rules))) (if (null next-rules) (zdd-set heads) (multiple-value-bind (term low high both) (partition-rules next-rules) ; (pr :rules rules) ; (pr :acch accumulated-heads) ; (pr :heads heads) ; (pr :next-rules next-rules) ; (pr :term term) ; (pr :low low) ; (pr :high high) ; (pr :both both) ; (break) (zdd-node term (recur (append (mapcar #'drop-first high) both) heads) (recur (append (mapcar #'drop-first low) both) heads)))))))