src/rule-trees.lisp @ 37e64253cccf

Stratify rules according to negations
author Steve Losh <steve@stevelosh.com>
date Sun, 20 Nov 2016 15:33:06 +0000
parents fcc52d00b79f
children addb56e3eb9d
(in-package :scully.rule-trees)
(in-readtable :fare-quasiquote)

;;;; Rule Trees ---------------------------------------------------------------
(adt:defdata rule-tree
  (node t rule-tree rule-tree)
  (top t)
  bottom)


(defun rule-head (rule)
  (first rule))

(defun rule-body (rule)
  (rest rule))

(defun-match bare-term (term)
  (`(ggp-rules::not ,contents) contents)
  (_ term))

(defun term< (a b)
  (< (bare-term a) (bare-term b)))


(defun find-smallest-body-term (bodies)
  (-<> bodies
    (mapcar #'first <>)
    (sort <> #'term<)
    (first <>)))

(defun partition (bodies)
  (let ((element (bare-term (find-smallest-body-term bodies))))
    (labels
        ((requires (body)
           (equal (first body) element))
         (disallows (body)
           (equal (first body) `(ggp-rules::not ,element)))
         (ignores (body)
           (not (or (requires body)
                    (disallows body)))))
      (values element
              (remove-if-not #'disallows bodies)
              (remove-if-not #'requires bodies)
              (remove-if-not #'ignores bodies)))))


(defun make-node (cache term hi lo)
  (if (eql hi lo)
    hi
    (ensure-gethash (list term hi lo) cache
                    (node term hi lo))))

(defun make-rule-tree (rules)
  (let* ((head (rule-head (first rules)))
         (top (top head))
         (cache (make-hash-table :test #'equal)))
    (recursively ((bodies (-<> rules
                            (mapcar #'rule-body <>)
                            (mapcar (rcurry #'sort #'term<) <>))))
      (cond
        ((null bodies) bottom)
        ((some #'null bodies) top)
        (t (multiple-value-bind (term disallows requires ignores)
               (partition bodies)
             (make-node cache
                        term
                        (recur (append (mapcar #'rest requires) ignores))
                        (recur (append (mapcar #'rest disallows) ignores)))))))))


;;;; GraphViz -----------------------------------------------------------------
(setf cl-dot:*dot-path* "/usr/local/bin/dot")

(defun attrs (object &rest attributes)
  (make-instance 'cl-dot:attributed
    :object object
    :attributes attributes))


(defmethod cl-dot:graph-object-node ((graph (eql 'rule-tree)) (object rule-tree))
  (make-instance 'cl-dot:node
    :attributes (adt:match rule-tree object
                  ((node term _ _) `(:label ,(aesthetic-string term)
                                     :shape :circle))

                  (bottom `(:label "⊥"
                            :shape :square))

                  ((top term) `(:label ,(aesthetic-string term)
                                :shape :rectangle)))))

(defmethod cl-dot:graph-object-points-to ((graph (eql 'rule-tree))
                                          (object rule-tree))
  (adt:match rule-tree object
    ((node _ hi lo) (list (attrs hi :style :solid)
                          (attrs lo :style :dashed)))
    ((top _) nil)
    (bottom nil)))


(defun draw (rule-tree &key (filename "rule-tree.png"))
  (cl-dot:dot-graph
    (cl-dot:generate-graph-from-roots 'rule-tree (list rule-tree))
    filename
    :format :png)
  rule-tree)


;;;; Scratch ------------------------------------------------------------------
(defparameter *rule* '(
                       (1000 1 2 (ggp-rules::not 3))
                       (1000 4 2 3 15)
                       (1000 (ggp-rules::not 19) 18)
                       (1000 19 17)
                       ))

(-<> *rule* make-rule-tree draw)