src/reasoners/zdd.lisp @ 51bc78b22d98
Rename the logic manager to just be the reasoner
author |
Steve Losh <steve@stevelosh.com> |
date |
Sun, 11 Dec 2016 16:32:58 -0500 |
parents |
src/logic.lisp@6ff8b64f6041 |
children |
41b2461432fc |
(in-package :scully.reasoners.zdd)
(defparameter *rules*
(scully.gdl::read-gdl "gdl/tictactoe-grounded.gdl")
; (scully.gdl::read-gdl "gdl/hanoi-grounded.gdl")
; (scully.gdl::read-gdl "gdl/8puzzle-grounded.gdl")
; (scully.gdl::read-gdl "gdl/roshambo2-grounded.gdl")
)
(defmacro defclass* (name-and-options direct-superclasses slots &rest options)
(flet ((slot-definition (conc-name slot)
(destructuring-bind (name &key
type
documentation
(accessor (symb conc-name name))
(initarg (intern (symbol-name name) :keyword)))
(ensure-list slot)
`(,name :initarg ,initarg :accessor ,accessor
,@(when type `(:type ,type))
,@(when documentation `(:documentation ,documentation))))))
(destructuring-bind (name &key (conc-name (symb name '-)))
(ensure-list name-and-options)
`(defclass ,name ,direct-superclasses
,(mapcar (curry #'slot-definition conc-name) slots)
,@options))))
(defclass* (zdd-reasoner :conc-name zr-) ()
(rules
roles
term->number
number->term
initial-zdd
legal-zdd
goal-zdd
terminal-zdd
possible-forest
happens-forest))
(defun find-initial-state (rules term->number)
(-<> rules
(mapcan (lambda-match
((list (list* 'ggp-rules::init body))
`((ggp-rules::true ,@body))))
<>)
(mapcar (lambda (term) (gethash term term->number)) <>)))
(defun find-roles (rules)
(mapcan (lambda-match
((list (list 'ggp-rules::role r))
(list r)))
rules))
(defun make-predicate-zdd (predicate term->number)
(-<> term->number
hash-table-alist
(remove-if-not (lambda (rule)
(eql predicate (first (first rule))))
<>)
(mapcar #'cdr <>)
(scully.zdd::zdd-set <>)))
(defun make-stratum-rule-trees (stratum)
(-<> stratum
(group-by #'car <>)
hash-table-values
(mapcar #'scully.rule-trees::make-rule-tree <>)))
(defun make-zdd-reasoner (rules)
"Turn a set of grounded GDL rules into a logic manager.
A rule forest is a collection of individual rule trees in a single layer,
stratified as necessary:
POSSIBLE: (STRATUM-1 STRATUM-2 ...)
HAPPENS: (STRATUM-1 STRATUM-2 ...)
|| ||
|| \/
|| (rule-tree-1 rule-tree-2 ...)
\/
(rule-tree-1 rule-tree-2 ...)
"
(let ((rules (scully.gdl::normalize-rules rules)))
(destructuring-bind (term->number number->term rule-layers)
(scully.terms::integerize-rules rules)
(flet ((make-forest (layer)
(-<> rule-layers
(gethash layer <>)
scully.terms::stratify-layer
(mapcar #'make-stratum-rule-trees <>))))
(scully.zdd::with-zdd
(make-instance 'zdd-reasoner
:rules rules
:roles (find-roles rules)
:possible-forest (make-forest :possible)
:happens-forest (make-forest :happens)
:initial-zdd (scully.zdd::zdd-set (find-initial-state rules term->number))
:legal-zdd (make-predicate-zdd 'ggp-rules::legal term->number)
:goal-zdd (make-predicate-zdd 'ggp-rules::goal term->number)
:terminal-zdd (make-predicate-zdd 'ggp-rules::terminal term->number)
:term->number term->number
:number->term number->term))))))
(defun initial-iset (reasoner)
"Return the initial information set of the game."
(zr-initial-zdd reasoner))
(defun number-to-term (reasoner number)
(gethash number (zr-number->term reasoner)))
(defun term-to-number (reasoner term)
(gethash term (zr-term->number reasoner)))
(defun rand-state (reasoner iset)
"Select a random member of the given information set."
(mapcar (curry #'number-to-term reasoner)
(scully.zdd::zdd-random-member iset)))
(defun terminalp (reasoner iset)
"Return whether the given information set is a terminal state."
(-<> iset
(scully.zdd::zdd-meet <> (zr-terminal-zdd reasoner))
scully.zdd::zdd-unit-p
not))
(defun draw-zdd (reasoner zdd)
(flet ((label (n)
(let ((*package* (find-package :ggp-rules)))
(-<> n
(number-to-term reasoner <>)
(structural-string <>)))))
(scully.graphviz::draw-zdd zdd :label-fn #'label)))
(defparameter *l* (make-zdd-reasoner *rules*))
; (defun apply-rule-tree (zdd rule-tree head-bound)
; "Apply the logical rules in `rule-tree` to the sets in `zdd`.
; `zdd` is assumed to contain sets of logical axioms. This function will update
; each of these sets to add any rule heads derivable from the axioms in the set.
; "
; (recursively ((zdd zdd)
; (rule-tree rule-tree))
; (ematch* (zdd rule-tree)
; ;; If Z = ∅ there are no sets to cons heads onto, bail.
; (((sink nil) _) zdd)
; ;; If R = ∅ or {∅} we've bottomed out of the rule tree and there are no
; ;; heads to cons, we're done.
; ((_ (sink)) zdd)
; ;; If we've passed the head boundary on the rule tree side then we're done
; ;; filtering and just need to cons in all the heads.
; ((_ (guard (node var _ _)
; (>= var head-bound)))
; (zdd-join zdd rule-tree))
; ;; If Z = {∅} we might have some heads we need to cons later in the rule
; ;; tree, so recur down the lo side of it.
; (((sink t) (node _ _ lo))
; (recur zdd lo))
; ;; Otherwise we need to filter.
; (((node var-z hi-z lo-z) (node var-r hi-r lo-r))
; (cond
; ((= var-z var-r) (zdd-node var-z
; (recur hi-z hi-r)
; (recur lo-z lo-r)))
; ((< var-z var-r) (zdd-node var-z
; (recur hi-z rule-tree)
; (recur lo-z rule-tree)))
; ((> var-z var-r) (recur zdd lo-r)))))))
;;;; PLAN
;;;
;;; 1. Receive GDL from server
;;; 2. Ground it
;;; 3. Integerize the ground GDL
;;; 4. Find initial state
;;; 5. Build rule trees for integerized rules
;;; 6. ...