;;; -*- Mode: Lisp; Package: Prolog; Base: 10. ; Options: ((world system) (Deterministic Always)) -*- ;;; (C) Copyright 1984,1985, Uppsala University ;;19-Dec-84, Mats Carlsson: ;;Fixed , fixed printing, fixed "thawing". ;;12-Sept-1984, Mats Carlsson: ;;Made FREEZE more sensible, added NOT, optimized det. proofs. ;;Empty list of constraints now supported. ;;Fixed :ordinary-term and generally cleaned up. ;;this implements a variant of Marsielle's Prolog II's geler in LM-Prolog ;;this version is deterministic ;;See LMP:LIBRARY;ARITHMETIC LISP and GRAPHICS-DEMOS LISP for examples of its use. ;;Here is some support for deterministic proofs. (defsubst prove-once-if-need-be (predications) (or (null predications) (prove-once predications))) (deffun prove-once (predications) (let* ((predication (first predications)) (interpreter-slot (definition-interpreter-slot (current-definition (first predication))))) (cond ((null (rest1 predications)) (prove predication interpreter-slot (continuation (true)))) ((interpreter-slot-deterministic interpreter-slot) (cond ((prove predication interpreter-slot (continuation (true))) (prove-once (rest1 predications))))) (t (prove predication interpreter-slot (continuation (funcall #'prove-once (rest1 predications)))))))) (eval-when (compile eval load) (defflavor constraint (cell ;A value cell whose contents is ;self (for unbound) ;another constraint (bound or unbound) ;or a term (bound). constraints ;The constraints to be satisfied. ;A list of predication. ) (prolog-flavor) :initable-instance-variables :gettable-instance-variables :settable-instance-variables)) (defmethod (constraint :unify) (other) (cond ;;Pass on if we have been forwarded. ((neq (contents cell) self) (unify (contents cell) other)) ;;We are not forwarded but other is constrained. ((typep other 'constraint) (let ((others-cell (send other ':cell))) (cond ;;Pass on if other has been forwarded. ;;Other may even have been forwarded to us! ((neq (contents others-cell) other) (unify self (contents others-cell))) ;;Otherwise, forward self to the other, and combine the constraints. (t (remind self ':reset-cell) (unify cell others-cell) (send other ':add-constraints constraints))))) ;;We are plainly constrained and the other is not constrained. (t (remind self ':reset-cell) (unify cell other) (with-new-*vector* (prove-once-if-need-be constraints))))) (defmethod (constraint :add-constraints) (predications) (cond ;;Pass on if we have been forwarded. ((neq (contents cell) self) (constrain-variable-1 (contents cell) predications)) ((with-new-*vector* (let* ((combined-constraints (combine-constraints constraints predications))) (selectq combined-constraints (:fail nil) (:invoke (prove-once-if-need-be predications)) (t (cond (t (remind self ':set-constraints constraints) (setq constraints combined-constraints) t))))))))) ;;This used to do an IDENTITY-UNION of the constraints. ;;I think that was due to a losing implementation of :THAW. (defun combine-constraints (constraints-1 constraints-2) (let ((both (%cell0)) (mark *trail*)) (cond ((funcall (current-entrypoint 'combine-constraints) (continuation (true)) both constraints-1 constraints-2) (%dereference both)) (t (untrail mark) (prolog-append constraints-1 constraints-2))))) (defmethod (constraint :reset-cell) () (setf (contents cell) self)) (defmethod (constraint :ordinary-term) () ;;This must NOT return self, would cause bad loops in the error-handler. (cond ((eq (contents cell) self) cell) ((typep (contents cell) 'prolog-flavor) (send (contents cell) ':ordinary-term)) (t (contents cell)))) (defvar *absurdity*) (let ((*prolog-work-area* *structure-area*)) (setq *absurdity* (make-instance-in-area *prolog-work-area* 'constraint ':cell (%cell0) ':constraints '((false))))) (rplacd (send *absurdity* ':cell) *absurdity*) (defmethod (constraint :thaw) (mode) (let ((mark *trail*)) (COND ((send self ':unify (%cell0)) (cond ((neq (contents cell) self) (lisp-form-1 (contents cell) mode)) (t self))) (T ;;Incompatible constraints that weren't detected until now. (untrail mark) *absurdity*)))) (defmethod (constraint :lisp-form) (mode) (cond ((neq (contents cell) self) (lisp-form-1 (contents cell) mode)) (t (selectq mode (:dont-invoke self) (:query (selectq (send self ':query-user) (no self) (yes (send self ':thaw mode)) (proceed (send self ':lisp-form ':invoke)))) (t (send self ':thaw mode)))))) (defvar *generating-lisp-form-of-constraints* nil) (defmethod (constraint :PRINT-SELF) (STREAM &REST ignore) (cond ((neq (contents cell) self) (FUNCALL (OR PRIN1 #'PRIN1) (contents cell) STREAM)) ((memq self *generating-lisp-form-of-constraints*) (FUNCALL (OR PRIN1 #'PRIN1) (lisp-form-1 cell ':print) STREAM)) (t (with-stack-list* (*generating-lisp-form-of-constraints* self *generating-lisp-form-of-constraints*) (FORMAT STREAM "#" (lisp-form-1 cell ':print) (cond ((null constraints) '(true)) ((cdr constraints) (prolog-cons 'and constraints)) ((car constraints)))))))) (defmethod (constraint :query-user) () (fquery '(:choices (((yes "Yes") #/y) ((no "No") #/n) ((proceed "Proceed") #/p)) :help-function (lambda (query-io ignore ignore) (format query-io "~%Type /"y/" to run the constraints. Type /"n/" to leave it alone. Or type /"p/" to run the constraints and any that pop up in the process."))) "~%There is a constrained variable, should I run its constraints? ")) (defmethod (constraint :variable-p) () (or (eq (contents cell) self) (unbound-variable-p (contents cell)))) ;;This was hairier and had the bug that occurences to SELF in CONSTRAINTS weren't preserved. (DEFMETHOD (CONSTRAINT :COPY) () (COND ((CDR (ASSQ SELF *CONSES-ALIST*))) (T (LET ((NEW-CELL (%CELL0))) (WITH-STACK-LIST* (PAIR SELF (MAKE-INSTANCE-IN-AREA *PROLOG-WORK-AREA* 'CONSTRAINT ':CELL NEW-CELL)) (WITH-STACK-LIST* (*CONSES-ALIST* PAIR *CONSES-ALIST*) (SETF (CONTENTS NEW-CELL) (COPY-TERM-1 (CONTENTS CELL))) (SEND (CDR PAIR) ':SET-CONSTRAINTS (COPY-TERM-1 CONSTRAINTS)) (CDR PAIR))))))) (defun constrain-variable-1 (variable predications) (cond ((typep variable 'constraint) (send variable ':add-constraints predications)) (t (unify variable (make-instance-in-area *prolog-work-area* 'constraint ':cell variable ':constraints predications))))) (define-predicate constrain (:options (:argument-list (variable predication)) (:documentation "postpones the execution of PREDICATION until VARIABLE has been instantiated.")) ((constrain ?variable ?goal) (cases ((variable ?variable) (lisp-predicate (constrain-variable-1 '?variable '(?goal)) :dont-invoke)) (?goal)))) (define-predicate freeze (:options (:argument-list (predication &optional count)) (:documentation "postpones the deterministic execution of PREDICATION until at most COUNT distinct variables are unbound.")) ((freeze ?goal) (freeze ?goal 0)) ((freeze ?goal ?n) (lisp-predicate (freeze-function '?goal '?n) :dont-invoke))) (define-predicate not (:options (:compile-method :open) (:argument-list (predication)) (:documentation "postpones the execution of (CANNOT-PROVE PREDICATION) until PREDICATION is ground")) ((not ?predication) (freeze (cannot-prove ?predication)))) (define-predicate  (:options (:argument-list (x y)) (:documentation "is true if X and Y do not unify.")) (( ?x ?y) (lisp-predicate (-function '?x '?y) :dont-invoke))) (defun freeze-function (goal count) (let ((vars (n-variables-in goal (1+ count)))) (cond (vars (let ((*predication* (prolog-list (prolog-list 'freeze goal count)))) (every vars #'(lambda (var) (constrain-variable-1 var *predication*))))) (t (with-stack-list (goals goal) (prove-once goals)))))) (defun n-variables-in (term count) (*catch 'n-variables-in (prog1 nil (n-variables-in-term term count ())))) (deffun n-variables-in-term (term count sofar) (cond ((consp term) (n-variables-in-term (cdr term) count (n-variables-in-term (car term) count sofar))) ((unbound-variable-p term) (or (memq term sofar) (push-in-area term sofar *prolog-work-area*)) (cond ((= count (length sofar)) (*throw 'n-variables-in sofar))) sofar) (t sofar))) (defun -function (x y) (multiple-value-bind (id-p some-x some-y) (identical-p x y) (cond ((not id-p) (and (cond ((unbound-variable-p some-x) (constrain-variable-1 some-x (prolog-list (prolog-list ' x y)))) (t t)) (cond ((unbound-variable-p some-y) (constrain-variable-1 some-y (prolog-list (prolog-list ' x y)))) (t t))))))) (defun prolog-append (list1 list2) (nconc (copylist* list1 *prolog-work-area*) list2)) (define-predicate combine-constraints (:options (:type :dynamic) (:place-clauses :after)) ;For manual's sake. ((combine-constraints :fail ?constraints ?) (member ( ?a ?b) ?constraints) (identical ?a ?b)) ((combine-constraints :fail ? ?constraints) (member ( ?a ?b) ?constraints) (identical ?a ?b)) ;;to be general this should really do a sort of UNION ((combine-constraints ?both (?constraint-1) (?constraint-2)) (or (*combine-constraints* ?both ?constraint-1 ?constraint-2) (*combine-constraints* ?both ?constraint-2 ?constraint-1)))) (define-predicate *combine-constraints* (:options (:type :dynamic)))