#| Copyright © 2018 by Pete Manolios CS 4820 Fall 2018. Pete Manolios. Homework 7. Due: 12/4 (Midnight) For this assignment, work in groups of 2-3. Send me exactly one solution per team and make sure to follow the submission instructions on the course Web page. In particular, make sure that the subject of your email submission is "CS 4820 HWK 7". The group members are: ... (put the names of the group members here) Use the same ACL2s build from homework 6. |# (set-ignore-ok t) ;; This gets us to raw lisp. :q #| The point of the next few forms is that we can use ACL2s from within lisp. That will be useful to check that your code works. |# (defun acl2s-last-result () (let ((state *the-live-state*)) (@ result))) #| If c is acl2s computation such as (+ 1 2) (append '(1 2) '(3 4)) etc. then the following form will ask ACL2 to evaluate c and will update the ACL2 global result to contain a list whose car is a flag indicating whether an error occurred, so nil means no error, and whose second element is the result of the computation, if no error occurred. |# (defun acl2s-compute (c) (let ((state *the-live-state*)) (multiple-value-bind (erp val state) (ld `((assign result ,c))) (if (equal val :eof) (ld `((assign result (list nil (@ result))))) (ld `((assign result (list t nil)))))) (acl2s-last-result))) #| Here are some examples (acl2s-compute '(+ 1 2)) (acl2s-compute '(append '(1 2) '(3 4))) (acl2s-compute '(+ 1 '(1 2))) |# #| If q is acl2s query that returns an error-triple such as (pbt 0) (test? (equal x x)) (thm (equal x x)) etc. then the following form will ask ACL2 to evaluate q and will update the ACL2 global result to contain a list whose car is a flag indicating whether an error occurred, so nil means no error, and whose second element is nil. |# (defun acl2s-query (q) (let ((state *the-live-state*)) (ld `((mv-let (erp val state) ,q (assign result (list erp nil))))) (acl2s-last-result))) #| Here are some examples you can try to see how acl2s-query works. (acl2s-query '(pbt 0)) (acl2s-query '(test? (equal x y))) (acl2s-query '(thm (equal x x))) (acl2s-query '(thm (iff (implies p q) (or (not p) q)))) |# #| A function that determines if f is a function defined in ACL2s and if so, its arity (number of arguments). If f is not a function, then the arity is nil. Otherwise, the arity is a natural number. Note that f can't be a macro. |# (defun acl2s-arity (f) (second (acl2s-compute `(arity ',f (w state))))) #| Some examples (acl2s-arity 'append) ; is nil since append is a macro (acl2s-arity 'binary-append) ; is 2 |# #| Next, we need to load some software libraries using quicklisp. For example, the trivia library provides pattern matching capabilities. Search for "match" below. You can see the online documentation for the other software libraries. |# (load "~/quicklisp/setup.lisp") (ql:quickload :trivia) (ql:quickload :cl-ppcre) (ql:quickload :let-plus) (setf ppcre:*allow-named-registers* t) #| We now define our own package. |# (defpackage :tp (:use :cl :trivia :ppcre :let-plus :acl2 :acl2s)) (in-package :tp) ;; We import acl2s-query into our package. (import '(acl2::acl2s-query acl2::acl2s-arity acl2::acl2s-compute)) #| We have a list of the propositional operators and information about them. :arity can be a positive integer or - (meaning arbitrary arity) If :arity is -, there must be an identity and the function must be associative and commutative. If :identity is non-nil, then the operator has the indicated identity. An operator is idempotent iff :idem is t. If :sink is not -, then it must be the case that (op ... sink ...) = sink, e.g., (and ... nil ...) = nil. FYI: it is common for global variables to be enclosed in *'s. |# (defparameter *p-ops* '((and :arity - :identity t :idem t :sink nil) (or :arity - :identity nil :idem t :sink t) (not :arity 1 :identity - :idem nil :sink -) (implies :arity 2 :identity - :idem nil :sink -) (iff :arity - :identity t :idem nil :sink -) (if :arity 3 :identity - :idem nil :sink -))) #| mapcar is like map. See the common lisp manual. In general if you have questions about lisp, ask on Piazza. |# (defparameter *p-funs* (mapcar #'car *p-ops*)) (defparameter *fo-quantifiers* '(forall exists)) (defparameter *booleans* '(t nil)) (defparameter *fo-keywords* (append *p-funs* *booleans* *fo-quantifiers*)) #| See the definition of member in the common lisp manual. Notice that there are different types of equality, including =, eql, eq, equal and equals. We need to be careful, so we'll just use equal and we'll define functions that are similar to the ACL2s functions we're familiar with. |# (defun in (a x) (member a x :test #'equal)) (defmacro len (l) `(length ,l)) (defun p-funp (x) (in x *p-funs*)) (defun get-alist (k l) (cdr (assoc k l :test #'equal))) (defun get-key (k l) (cadr (member k l :test #'equal))) (defun remove-dups (l) (remove-duplicates l :test #'equal)) (defmacro == (x y) `(equal ,x ,y)) (defmacro != (x y) `(not (equal ,x ,y))) (defun booleanp (x) (in x *booleans*)) (defun no-dupsp (l) (or (endp l) (and (not (in (car l) (cdr l))) (no-dupsp (cdr l))))) (defun pfun-argsp (pop args) (and (p-funp pop) (let ((arity (get-key :arity (get-alist pop *p-ops*)))) (and (or (== arity '-) (== (len args) arity)) (every #'p-formulap args))))) #| Next we have some utilities for converting propositional formulas to ACL2 formulas. |# (defun to-acl2 (f) (match f ((type symbol) (intern (symbol-name f) "ACL2")) ((cons x xs) (mapcar #'to-acl2 f)) (_ f))) #| A FO term is either a constant symbol: a symbol whose name starts with "C" and is optionally followed by a natural number with no leading 0's, e.g., c0, c1, c101, c, etc are constant symbols, but c00, c0101, c01, etc are not. Notice that (gentemp "C") will create a new constant. Notice that symbol names are case insensitive, so C1 is the same as c1. quoted constant: anything of the form (quote object) for any object constant object: a rational, boolean, string, character or keyword variable: a symbol whose name starts with "X", "Y", "Z", "W", "U" or "V" and is optionally followed by a natural number with no leading 0's (see constant symbol). Notice that (gentemp "X") etc will create a new variable. function application: (f t1 ... tn), where f is a non-constant/non-variable/non-boolean symbol starting with a letter (A-Z). The arity of f is n and every occurrence of (f ...) in a term or formula has to have arity n. Also, if f is a defined function in ACL2s, its arity has to match what ACL2s expects. We allow functions of 0-arity. |# (defun char-nat-symbolp (s chars) (and (symbolp s) (let ((name (symbol-name s))) (and (<= 1 (len name)) (in (char name 0) chars) (or (== 1 (len name)) (let ((i (parse-integer name :start 1))) (and (integerp i) (<= 0 i) (string= (format nil "~a~a" (char name 0) i) name)))))))) (defun constant-symbolp (s) (char-nat-symbolp s '(#\C))) (defun variable-symbolp (s) (char-nat-symbolp s '(#\X #\Y #\Z #\W #\U #\V))) (defun quotep (c) (and (consp c) (== (car c) 'quote))) (defun constant-objectp (c) (typep c '(or boolean rational simple-string standard-char keyword))) #| Examples (constant-objectp #\a) (constant-objectp 0) (constant-objectp 1/221) (constant-objectp "hi there") (constant-objectp t) (constant-objectp nil) (constant-objectp :hi) (constant-objectp 'a) (quotep '1) ; recall that '1 is evaluated first (quotep ''1) ; but this works (quotep '(1 2 3)) ; similar to above (quotep ''(1 2 3)) ; similar to above (quotep (list 'quote (list 1 2 3))) ; verbose version of previous line |# (defun function-symbolp (f) (and (symbolp f) (not (in f *fo-keywords*)) (not (keywordp f)) (not (constant-symbolp f)) (not (variable-symbolp f)))) #| (function-symbolp 'c) (function-symbolp 'c0) (function-symbolp 'c00) (function-symbolp 'append) (function-symbolp '+) |# (defmacro mv-and (a b &optional (fsig 'fsig) (rsig 'rsig)) `(if ,a ,b (values nil ,fsig ,rsig))) (defmacro mv-or (a b &optional (fsig 'fsig) (rsig 'rsig)) `(if ,a (values t ,fsig ,rsig) ,b)) (defun fo-termp (term &optional (fsig nil) (rsig nil)) (match term ((satisfies constant-symbolp) (values t fsig rsig)) ((satisfies variable-symbolp) (values t fsig rsig)) ((satisfies quotep) (values t fsig rsig)) ((satisfies constant-objectp) (values t fsig rsig)) ((list* f args) (mv-and (and (function-symbolp f) (not (get-alist f rsig))) (let* ((fsig-arity (get-alist f fsig)) (acl2s-arity (or fsig-arity (acl2s-arity (to-acl2 f)))) (arity (or acl2s-arity (len args))) (fsig (if fsig-arity fsig (acons f arity fsig)))) (mv-and (== arity (len args)) (fo-termsp args fsig rsig))))) (_ (values nil fsig rsig)))) (defun fo-termsp (terms fsig rsig) (mv-or (endp terms) (let+ (((&values res fsig rsig) (fo-termp (car terms) fsig rsig))) (mv-and res (fo-termsp (cdr terms) fsig rsig))))) #| Examples (fo-termp '(f d 2)) (fo-termp '(f c 2)) (fo-termp '(f c0 2)) (fo-termp '(f c00 2)) (fo-termp '(f '1 '2)) (fo-termp '(f (f '1 '2) (f v1 c1 '2))) (fo-termp '(binary-append '1 '2)) (fo-termp '(binary-append '1 '2 '3)) (fo-termp '(binary-+ '1 '2)) (fo-termp '(+ '1 '2)) (fo-termp '(- '1 '2)) |# #| A FO atomic formula is either an atomic equality: (= t1 t2), where t1, t2 are FO terms. atomic relation: (P t1 ... tn), where P is a non-constant/non-variable symbol. The arity of P is n and every occurrence of (P ...) has to have arity n. Also, if P is a defined function in ACL2s, its arity has to match what ACL2s expects. We do not check that if P is a defined function then it has to return a Boolean. Make sure do not use such examples. |# (defun relation-symbolp (f) (function-symbolp f)) #| Examples (relation-symbolp '<) (relation-symbolp '<=) (relation-symbolp 'binary-+) |# (defun fo-atomic-formulap (f &optional (fsig nil) (rsig nil)) (match f ((list '= t1 t2) (fo-termsp (list t1 t2) fsig rsig)) ((list* r args) (mv-and (and (relation-symbolp r) (not (get-alist r fsig))) (let* ((rsig-arity (get-alist r rsig)) (acl2s-arity (or rsig-arity (acl2::acl2s-arity (to-acl2 r)))) (arity (or acl2s-arity (len args))) (rsig (if rsig-arity rsig (acons r arity rsig)))) (mv-and (== arity (len args)) (fo-termsp args fsig rsig))))) (_ (values nil fsig rsig)))) #| Here is the definition of a propositional formula. We allow Booleans. |# (defun pfun-fo-argsp (pop args fsig rsig) (mv-and (p-funp pop) (let ((arity (get-key :arity (get-alist pop *p-ops*)))) (mv-and (or (== arity '-) (== (len args) arity)) (fo-formulasp args fsig rsig))))) (defun p-fo-formulap (f fsig rsig) (match f ((type boolean) (values t fsig rsig)) ((list* pop args) (if (p-funp pop) (pfun-fo-argsp pop args fsig rsig) (values nil fsig rsig))) (_ (values nil fsig rsig)))) #| Here is the definition of a quantified formula. The quantified variables can be a variable or a non-empty list of variables with no duplicates. Examples include (exists w (P w y z x)) (exists (w) (P w y z x)) (forall (x y z) (exists w (P w y z x))) But this does not work (exists c (P w y z x)) (forall () (exists w (P w y z x))) (forall (x y z x) (exists w (P w y z x))) |# (defun quant-fo-formulap (f fsig rsig) (match f ((list q vars body) (mv-and (and (in q *fo-quantifiers*) (or (variable-symbolp vars) (and (consp vars) (no-dupsp vars) (every #'variable-symbolp vars)))) (fo-formulap body fsig rsig))) (_ (values nil fsig rsig)))) (defun mv-seq-first-fun (l) (if (endp (cdr l)) (car l) (let ((res (gensym)) (f (gensym)) (r (gensym))) `(multiple-value-bind (,res ,f ,r) ,(car l) (if ,res (values t ,f ,r) ,(mv-seq-first-fun (cdr l))))))) (defmacro mv-seq-first (&rest rst) (mv-seq-first-fun rst)) (defun fo-formulap (f &optional (fsig nil) (rsig nil)) (mv-seq-first (fo-atomic-formulap f fsig rsig) (p-fo-formulap f fsig rsig) (quant-fo-formulap f fsig rsig) (values nil fsig rsig))) (defun fo-formulasp (fs fsig rsig) (mv-or (endp fs) (let+ (((&values res fsig rsig) (fo-formulap (car fs) fsig rsig))) (mv-and res (fo-formulasp (cdr fs) fsig rsig))))) #| We can use fo-formulasp to find the function and relation symbols in a formula as follows. |# (defun fo-f-symbols (f) (let+ (((&values res fsig rsig) (fo-formulap f))) (mapcar #'car fsig))) (defun fo-r-symbols (f) (let+ (((&values res fsig rsig) (fo-formulap f))) (mapcar #'car rsig))) #| Examples (fo-formulap '(forall (x y z) (exists w (P w y z x)))) (fo-formulap '(forall (x y z x) (exists w (P w y z x)))) (quant-fo-formulap '(forall (x y z) (exists y (P w y z x))) nil nil) (fo-formulap '(exists w (P w y z x))) (fo-atomic-formulap '(exists w (P w y z x)) nil nil) (quant-fo-formulap '(exists w (P w y z x)) nil nil) (fo-formulap '(P w y z x)) (fo-formulap '(and (forall (x y z) (or (not (= (q z) (r z))) nil (p '1 x y))) (exists w (implies (forall x1 (iff (= (p1 x1 w) c2) (q c1) (r c2))) (p '2 y w))))) (fo-formulap '(forall (x y z) (or (not (= (q z) (r z))) nil (p '1 x y)))) (fo-formulap '(exists w (implies (forall x1 (iff (= (p1 x1 w) c2) (q c1) (r c2))) (p '2 y w)))) (fo-formulap '(exists w (implies (forall x1 (iff (p1 x1 w) (q c1) (r c2))) (p '2 y w)))) (fo-formulap '(and (forall (x y z) (or (not (= (q2 z) (r2 z))) nil (p '1 x y))) (exists w (implies (forall x1 (iff (= (p1 x1 w) c2) (q c1) (r c2))) (p '2 y w))))) (fo-formulap '(forall x1 (iff (p1 x1 w) (q c1) (r c2)))) (fo-formulap '(iff (p1 x1 w) (q c1) (r c2))) (fo-atomic-formulap '(p1 x1 w)) (variable-symbolp 'c1) (fo-termp 'x1) (fo-termp 'w1) (fo-termp '(x1 w) nil nil) (fo-termsp '(x1 w) nil nil) |# #| Where appropriate, for the problems below, modify your solutions from homework 6. For example, you already implemented most of the simplifications in Question 1 in homework 6. |# #| Question 1. (25 pts) Define function fo-simplify that given a first-order (FO) formula returns an equivalent FO formula with the following properties. A. The returned formula is either a constant or does not include any constants. For example: (and (p x) t (q t y) (q y z)) should be simplified to (and (p x) (q t y) (q y z)) (and (p x) t (q t b) nil) should be simplified to nil B. Expressions are flattened, e.g.: (and (p c) (= c '1) (and (r) (s) (or (r1) (r2)))) is not flat, but this is (and (p c) (= c '1) (r) (s) (or (r1) (r2))) A formula of the form (op ...) where op is a Boolean operator applied to 0 or 1 arguments is not flat. For example, replace (and) with t. A formula of the form (op ... (op ...)) where op is a Boolean operator is not flat. For example, replace (and p q (and r s)) with (and p q r s). C. If there is Boolean constant s s.t. If (op ... s ...) = s, then we say that s is a sink of op. For example t is a sink of or. A formula is sink-free if no such subformulas remain. The returned formula should be sink-free. D. Simplify your formulas so that no subexpressions of the following form remain (where f is a formula) (not (not f)) E. Simplify formulas so that no subexpressions of the following form remain (op ... p ... q ...) where p, q are equal literals or p = (not q) or q = (not p). For example (or (f) (f1) (p a b) (not (p a b)) (= w z)) should be simplified to t F. Simplify formulas so there are no vacuous quantified formulas. For example, (forall (x w) (P y z)) should be simplified to (P y z) and (forall (x w) (P x y z)) should be simplified to (forall (x) (P x y z)) G. Simplify formulas by using ACL2s to evaluate, when possible, terms of the form (f ...) where f is an ACL2s function all of whose arguments are either constant-objects or quoted objects. For example, (P (binary-+ 4 2) 3) should be simplified to (P 6 3) Hint: use acl2s-compute and to-acl2. For example, consider (acl2s-compute (to-acl2 '(binary-+ 4 2))) On the other hand, (P (binary-+ 'a 2) 3) does not get simplified because (acl2s-compute (to-acl2 '(binary-+ 'a 2))) indicates an error (contract/guard violation). See the definition of acl2s-compute to see how to determine if an error occurred. H. Test your definitions using at least 10 interesting formulas. Use the acl2s code, if you find it useful. Include deeply nested formulas, all of the Boolean operators, quantified formulas, etc. |# (defun fo-simplify (f) ...) #| Question 2. (10 pts) Define nnf, a function that given a FO formula, something that satisfies fo-formulap, puts it into negation normal form (NNF). The resulting formula cannot contain any of the following propositional connectives: implies, iff, if. Test nnf using at least 10 interesting formulas. Make sure you support quantification. |# (defun nnf (f) ...) #| Question 3. (25 pts) Define simp-skolem-pnf-cnf, a function that given a FO formula, simplifies it using fo-simplify, then puts it into negation normal form, applies skolemization, then puts the formula in prenex normal form and finally transforms the matrix into an equivalent CNF formula. To be clear: The formula returned should be equi-satisfiable with the input formula, should contain no existential quantifiers, and if it has quantifiers it should be of the form (forall (...) matrix) where matrix is quantifier-free and in CNF. Test your functions using at least 10 interesting formulas. |# (defun simp-skolem-pnf-cnf (f) ...) #| Question 4. (15 pts) Define unify, a function that given an a non-empty list of pairs, where every element of the pair is FO-term, returns an mgu (most general unifier) if one exists or the symbol 'fail otherwise. An assignment is a list of conses, where car is a variable, the cdr is a term and the variables (in the cars) are unique. Test your functions using at least 10 interesting inputs. |# (defun unify (l) ...) #| Question 5. (25 pts) Define fo-no=-val, a function that given a FO formula, without equality, checks if it is valid using U-Resolution. If it is valid, return 'valid. Your code should use positive resolution and must implement subsumption and replacement. Test your functions using at least 10 interesting inputs including the formulas from the following pages of the book: 178 (p38, p34), 179 (ewd1062), 180 (barb), and 198 (the Los formula). |# (defun fo-no=-val (f) ...) #| Question 6. Extra Credit (15 pts) Define fo-val, a function that given a FO formula, checks if it is valid using U-Resolution. If it is valid, return 'valid. Your code should use positive resolution and must implement subsumption and replacement. This is an extension of question 5, where you replace equality with a new relation symbol and add the appropriate equivalence and congruence hypotheses. |# (defun fo-val (f) ...) #| Question 7. Extra Credit (25 pts) Define horn-sat. It is a function that takes as input (1) hs, a list of positive universal Horn sentences and (2) e, a sentence of the form (exists vars (and ...)) where ... is a list of atomic formulas You can assume none of the inputs formulas contain =, if you wish. If you solved q5, you can also deal with =. If hs |= e, then horn-sat returns an assignment, sigma, s.t. hs |= (and ...)sigma Recall that an assignment is defined in Q4. |# (defun horn-sat (hs e) ...)