Included Books
other
(in-package "ACL2")
include-book
(include-book "std/util/define" :dir :system)
include-book
(include-book "../misc/hons-alphorder-merge")
other
(define aig-var-p (x) :inline t (if (atom x) (and (not (eq x t)) (not (eq x nil))) (and (eq (car x) nil) (cdr x) t)) /// (defthm aig-var-p-of-aig-var (implies x (aig-var-p (cons nil x)))) (defthm aig-var-p-of-atom (implies (not (aig-var-p x)) (not (and (atom x) (not (booleanp x))))) :rule-classes :compound-recognizer))
other
(define aig-var-listp (x) (if (atom x) (eq x nil) (and (aig-var-p (car x)) (aig-var-listp (cdr x)))) /// (defthmd aig-var-p-when-member-of-aig-var-listp (implies (and (aig-var-listp x) (member k x)) (aig-var-p k))) (defthm aig-var-listp-of-cons (equal (aig-var-listp (cons x y)) (and (aig-var-p x) (aig-var-listp y)))) (defthm aig-var-listp-of-nil (aig-var-listp nil)) (defthm aig-var-listp-of-insert (implies (and (aig-var-p x) (aig-var-listp y)) (aig-var-listp (insert x y))) :hints (("Goal" :in-theory (enable insert head tail sfix)))) (defthm aig-var-listp-of-union (implies (and (aig-var-listp x) (aig-var-listp y)) (aig-var-listp (union x y))) :hints (("Goal" :in-theory (enable union head tail sfix)))))
other
(define aig-atom-p (x) :inline t (or (atom x) (and (eq (car x) nil) (cdr x) t)) /// (defthm not-aig-atom-implies-consp (implies (not (aig-atom-p x)) (consp x)) :rule-classes :compound-recognizer) (defthm aig-var-p-when-aig-atom-p (implies (and (aig-atom-p x) (not (booleanp x))) (aig-var-p x)) :hints (("Goal" :in-theory (enable aig-var-p)))) (defthm aig-atom-p-when-aig-var-p (implies (aig-var-p x) (aig-atom-p x)) :hints (("Goal" :in-theory (enable aig-var-p)))) (defthm not-aig-atom-p-of-negation (not (aig-atom-p (cons x nil)))) (defthm not-aig-atom-p-of-cons (implies x (not (aig-atom-p (cons x y))))) (defthmd aig-atom-p-of-cons-strong (iff (aig-atom-p (cons x y)) (and (not x) y)) :hints (("Goal" :in-theory (enable aig-atom-p)))))
other
(defsection aig-cases :parents (aig-other) :short "Control-flow macro to split into cases on what kind of AIG you have encountered." :long "@(def aig-cases)" (defmacro aig-cases (x &key true false var inv and) `(let ((aig-cases-var ,X)) (cond ((aig-atom-p aig-cases-var) (cond ((eq aig-cases-var t) ,TRUE) ((eq aig-cases-var nil) ,FALSE) (t ,VAR))) ((eq (cdr aig-cases-var) nil) ,INV) (t ,AND)))))
other
(defsection aig-env-lookup-missing-output :parents (aig-eval) :short "Stub for warnings about missing variables in AIG evaluation." :long "<p>This stub is called when @(see aig-eval) encounters a variable in the AIG that has no binding in the environment. It is generally configured with @(see aig-env-lookup-missing-action).</p>" (defstub aig-env-lookup-missing-output (name) nil))
other
(defsection aig-env-lookup-missing-action :parents (aig-eval) :short "Configurable warnings about missing variables in AIG evaluation." :long "<p>Ordinarily @(see aig-eval) treats any variables that are not bound in the environment as having value @('t'). But a missing bindings could be the result of a bug in your program, so by default @('aig-eval') is set up to print a warning if this happens.</p> <p>@(call aig-env-lookup-missing-action) allows you to control whether these warnings are printed, and also whether @(see break$) should be called. The valid @('action')s are:</p> <ul> <li>@('nil'), silently bind the variable to @('t'),</li> <li>@(':warn') (the default), print a warning but do not @('break$'), and</li> <li>@(':break'), to print the warning and then call @('break$').</li> </ul>" (defconst *aig-env-lookup-warn-missing-binding* t) (defun aig-env-lookup-missing-quiet (name) (declare (xargs :guard t) (ignore name)) nil) (defun aig-env-lookup-missing-complain (name) (declare (xargs :guard t)) (and *aig-env-lookup-warn-missing-binding* (cw "WARNING: Missing variable binding ~x0 in AIG-ENV-LOOKUP; ~ assigning T~%" name))) (local (in-theory (disable (break$)))) (defun aig-env-lookup-missing-break (name) (declare (xargs :guard t)) (and *aig-env-lookup-warn-missing-binding* (prog2$ (cw "WARNING: Missing variable binding ~x0 in ~x1; assigning ~ T. To avoid this break, run ~x2, where action is NIL or ~ :WARN.~%" name 'aig-env-lookup '(aig-env-lookup-missing-action action)) (break$)))) (defmacro aig-env-lookup-missing-action (val) (case val ((nil) '(defattach aig-env-lookup-missing-output aig-env-lookup-missing-quiet)) (:warn '(defattach aig-env-lookup-missing-output aig-env-lookup-missing-complain)) (:break '(defattach aig-env-lookup-missing-output aig-env-lookup-missing-break)) (t (er hard 'aig-env-lookup-missing-action "Expected argument NIL, :WARN, or :BREAK.~%")))) (aig-env-lookup-missing-action :warn))
other
(define aig-env-lookup :parents (aig-eval) :short "Look up the value of an AIG variable in an environment." ((x "Variable to look up.") (env "Fast alist mapping variables to values.")) :long "<p>Unbound variables are given the default value @('t') instead of @('nil') because this makes theorems about @(see faig) evaluation work out more nicely (it makes unbound FAIG variables evaluate to @('X')).</p>" :enabled t (let ((look (hons-get x env))) (if look (and (cdr look) t) (mbe :logic t :exec (if *aig-env-lookup-warn-missing-binding* (prog2$ (aig-env-lookup-missing-output x) t) t)))))
other
(define aig-alist-lookup :parents (aig-eval) :short "Look up the value of an AIG variable in an environment." ((x "Variable to look up.") (env "Fast alist mapping variables to values.")) :long "<p>Unbound variables are given the default value @('t') instead of @('nil') because this makes theorems about @(see faig) evaluation work out more nicely (it makes unbound FAIG variables evaluate to @('X')).</p>" :enabled t (let ((look (hons-get x env))) (if look (cdr look) (mbe :logic t :exec (if *aig-env-lookup-warn-missing-binding* (prog2$ (aig-env-lookup-missing-output x) t) t)))))
other
(define aig-eval :parents (aig-semantics) :short "@(call aig-eval) gives the semantics of @(see AIG)s: it gives the Boolean value of the AIG @('x') under the environment @('env')." ((x "The AIG to evaluate.") (env "A fast alist that binds variables to values. Typically it should bind every variable in @('x') to some Boolean value. When this isn't the case, variables are assigned the default value @('t'); see @(see aig-env-lookup).")) :long "<p>This function is @(see memoize)d. You should typically free its memo table after you are done with whatever @('env') you are using, to avoid excessive memory usage. (We don't use @(':forget t') because you often want to evaluate several related AIGs.)</p>" :enabled t (aig-cases x :true t :false nil :var (aig-env-lookup x env) :inv (not (aig-eval (car x) env)) :and (and (aig-eval (car x) env) (aig-eval (cdr x) env))) /// (memoize 'aig-eval :condition '(and (consp x) (cdr x))))
other
(define aig-eval-list :parents (aig-semantics) :short "@(call aig-eval-list) evaluates a list of AIGs." ((x "The AIG list to evaluate.") (env "The environment to use; see @(see aig-eval).")) :returns (vals "A list of Boolean values; the evaluations of each AIG under this environment.") :enabled t (if (atom x) nil (cons (aig-eval (car x) env) (aig-eval-list (cdr x) env))) /// (defthm consp-of-aig-eval-list (equal (consp (aig-eval-list x env)) (consp x))) (defthm len-of-aig-eval-list (equal (len (aig-eval-list x env)) (len x))) (defthm aig-eval-list-of-append (equal (aig-eval-list (append x y) env) (append (aig-eval-list x env) (aig-eval-list y env)))))
other
(define aig-eval-alist :parents (aig-semantics) :short "@(call aig-eval-alist) evaluates an AIG Alist (an alist binding keys to AIGs)." ((x "The AIG alist to evaluate. This does not need to be a fast alist.") (env "The environment to use; see @(see aig-eval).")) :returns (vals-alist "An ordinary (slow) alist that binds the same keys to the values of their associated AIGs.") :enabled t (cond ((atom x) nil) ((atom (car x)) (aig-eval-alist (cdr x) env)) (t (cons (cons (caar x) (aig-eval (cdar x) env)) (aig-eval-alist (cdr x) env)))) /// (defthm hons-assoc-equal-aig-eval-alist (equal (hons-assoc-equal key (aig-eval-alist x env)) (and (hons-assoc-equal key x) (cons key (aig-eval (cdr (hons-assoc-equal key x)) env)))) :hints (("Goal" :induct t))))
other
(define aig-eval-alists :parents (aig-semantics) :short "Evaluate a list of AIG Alists." ((x "List of AIG Alists to evaluate.") (env "The environment to use; see @(see aig-eval).")) :returns (vals-alists "A copy of @('x'), except that each AIG has been replaced with its value.") :enabled t (if (atom x) nil (cons (aig-eval-alist (car x) env) (aig-eval-alists (cdr x) env))))
other
(define aig-vars (x) :parents (aig) :short "@(call aig-vars) returns the variables of the AIG @('X')." :returns (vars "An ordered set of AIG variables (atoms).") :long "<p>Note: variable collection can be surprisingly tricky to do efficiently. For a good background discussion that describes various approaches to the problem and ways to avoid needing to collect variables, see @(see 4v-sexpr-vars).</p> <p>@('aig-vars') is a slow but simple way to collect the variables that occur within an AIG, and we adopt it as our <b>normal form</b> for talking about the variables of an AIG. That is, when we introduce other, faster algorithms for collecting variables, we always relate them back to @('aig-vars').</p> <p>The variable collection strategy used by @('aig-vars') is to memoize the whole computation; this implicitly records, for every AND node, the exact set of variables that are found under that node. We use ordinary @(see std/osets) as our variable set representation so that merging these sets is linear at each node. The overall complexity is then @('O(n^2)') in the size of the AIG.</p> <p>This approach records the full variable information for every AND node throughout the AIG. This takes a lot of memory, and often you do not need nearly this much information. In practice, functions like @(see aig-vars-1pass) are often much more practical.</p>" :verify-guards nil :enabled t (aig-cases x :true nil :false nil :var (mbe :logic (insert x nil) :exec (hons x nil)) :inv (aig-vars (car x)) :and (union (aig-vars (car x)) (aig-vars (cdr x)))) /// (defthm aig-var-listp-aig-vars (aig-var-listp (aig-vars x))) (defthm true-listp-aig-vars (true-listp (aig-vars x)) :rule-classes :type-prescription) (defthm setp-aig-vars (setp (aig-vars x)) :hints (("Goal" :in-theory (enable* (:ruleset primitive-rules))))) (verify-guards aig-vars :hints (("Goal" :in-theory (enable* (:ruleset primitive-rules))))) (memoize 'aig-vars :condition '(and (consp x) (cdr x))))
other
(define aig-not (x) :parents (aig-constructors) :short "@(call aig-not) constructs an AIG representing @('(not x)')." :long "<p>This could be implemented as @('(hons x nil)'), but we at least take care to fold constants and avoid creating double negatives.</p>" :returns aig (cond ((eq x nil) t) ((eq x t) nil) ((and (not (aig-atom-p x)) (eq (cdr x) nil)) (car x)) (t (hons x nil))) /// (defthm aig-eval-not (equal (aig-eval (aig-not x) env) (not (aig-eval x env)))))
other
(define aig-and-count (x) :parents (aig) :short "Counts how many ANDs are in an AIG." :returns (count natp :rule-classes :type-prescription) (cond ((aig-atom-p x) 0) ((eq (cdr x) nil) (aig-and-count (car x))) (t (+ 1 (aig-and-count (car x)) (aig-and-count (cdr x))))) /// (defthm aig-and-count-when-atom (implies (aig-atom-p x) (equal (aig-and-count x) 0))) (defthm aig-and-count-of-aig-not (equal (aig-and-count (aig-not x)) (aig-and-count x)) :hints (("Goal" :in-theory (enable aig-not)))))
local
(local (set-default-parents aig-and))
other
(define aig-negation-p (x y) :short "@(call aig-negation-p) determines if the AIGs @('x') and @('y') are (syntactically) negations of one another." :inline t (or (and (consp y) (eq (cdr y) nil) (hons-equal (car y) x)) (and (consp x) (eq (cdr x) nil) (hons-equal (car x) y))) /// (defthm aig-negation-p-symmetric (equal (aig-negation-p x y) (aig-negation-p y x))) (defthmd aig-negation-p-correct-1 (implies (and (aig-negation-p x y) (aig-eval x env)) (equal (aig-eval y env) nil))) (defthmd aig-negation-p-correct-2 (implies (and (aig-negation-p x y) (not (aig-eval x env))) (equal (aig-eval y env) t))))
other
(define aig-and-dumb (x y) :short "@(call aig-and-dumb) is a simpler alternative to @(see aig-and)." :long "<p>This does far fewer reductions than @(see aig-and). We fold constants and collapse @('x & x') and @('x & ~x'), but that's it.</p>" :returns aig (cond ((or (eq x nil) (eq y nil)) nil) ((eq x t) y) ((eq y t) x) ((hons-equal x y) x) ((aig-negation-p x y) nil) (t (hons x y))) /// (defthm aig-eval-of-aig-and-dumb (equal (aig-eval (aig-and-dumb x y) env) (and (aig-eval x env) (aig-eval y env)))) (defthm aig-and-dumb-of-constants (and (equal (aig-and-dumb nil x) nil) (equal (aig-and-dumb x nil) nil) (equal (aig-and-dumb x t) x) (equal (aig-and-dumb t x) x))))
other
(define aig-and-pass1 (x y) :returns (mv hit ans) :short "Level 1 simplifications." :long "<p>See also @(see aig-and-dumb), which tries to apply these same reductions, but otherwise just gives up, and doesn't report whether it has succeded or not.</p>" :inline t (cond ((eq x nil) (mv t nil)) ((eq y nil) (mv t nil)) ((eq x t) (mv t y)) ((eq y t) (mv t x)) ((hons-equal x y) (mv t x)) ((aig-negation-p x y) (mv t nil)) (t (mv nil nil))) /// (defret aig-and-pass1-correct (implies hit (equal (aig-eval ans env) (and (aig-eval x env) (aig-eval y env)))) :rule-classes nil))
other
(define aig-and-pass2a (x y) :returns (mv status arg1 arg2) :short "Level 2 Contradiction Rule 1 and Idempotence Rule, Single Direction." (b* (((unless (and (not (aig-atom-p x)) (not (eq (cdr x) nil)))) (mv :fail x y)) (a (car x)) (b (cdr x)) ((when (or (aig-negation-p a y) (aig-negation-p b y))) (mv :subterm nil nil)) ((when (or (hons-equal a y) (hons-equal b y))) (mv :subterm x x))) (mv :fail x y)) /// (defret aig-and-pass2a-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil) (defret aig-and-pass2a-never-reduced (not (equal status :reduced))) (defret aig-and-pass2a-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-pass2a-normalize-status (implies (and (not (equal status :subterm)) (not (equal status :reduced))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))))
other
(define aig-and-pass2 (x y) :returns (mv status arg1 arg2) :short "Level 2 Contradiction Rule 1 and Idempotence Rule, Both Directions." :inline t (b* (((mv status a b) (aig-and-pass2a y x)) ((unless (eq status :fail)) (mv status a b))) (aig-and-pass2a x y)) /// (defret aig-and-pass2-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil :hints (("Goal" :use ((:instance aig-and-pass2a-correct) (:instance aig-and-pass2a-correct (x y) (y x)))))) (defret aig-and-pass2-never-reduced (not (equal status :reduced))) (defret aig-and-pass2-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-pass2-normalize-status (implies (and (not (equal status :subterm)) (not (equal status :reduced))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))))
other
(define aig-and-pass3 (x y) :returns (mv status arg1 arg2) :short "Level 2 Contradiction Rule 2 and all Level 4 Rules." :inline t (b* (((unless (and (not (aig-atom-p x)) (not (eq (cdr x) nil)))) (mv :fail x y)) ((unless (and (not (aig-atom-p y)) (not (eq (cdr y) nil)))) (mv :fail x y)) (a (car x)) (b (cdr x)) (c (car y)) (d (cdr y)) ((when (or (aig-negation-p a c) (aig-negation-p a d) (aig-negation-p b c) (aig-negation-p b d))) (mv :subterm nil nil)) ((when (hons-equal a c)) (mv :reduced x d)) ((when (hons-equal b c)) (mv :reduced x d)) ((when (hons-equal b d)) (mv :reduced x c)) ((when (hons-equal a d)) (mv :reduced x c))) (mv :fail x y)) /// (defret aig-and-pass3-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil) (defret aig-and-pass3-reduces-count (implies (eq status :reduced) (< (+ (aig-and-count arg1) (aig-and-count arg2)) (+ (aig-and-count x) (aig-and-count y)))) :rule-classes nil :hints (("Goal" :in-theory (enable aig-and-count)))) (defret aig-and-pass3-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-pass3-normalize-status (implies (and (not (equal status :subterm)) (not (equal status :reduced))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))))
other
(define aig-and-pass4a (x y) :returns (mv status arg1 arg2) :short "Level 2, Subsumption Rules 1 and 2, Single Direction." (b* (((unless (and (not (aig-atom-p x)) (eq (cdr x) nil))) (mv :fail x y)) (~x (car x)) ((unless (and (not (aig-atom-p ~x)) (not (eq (cdr ~x) nil)))) (mv :fail x y)) (a (car ~x)) (b (cdr ~x)) ((when (or (aig-negation-p a y) (aig-negation-p b y))) (mv :subterm y y)) ((when (and (not (aig-atom-p y)) (not (eq (cdr y) nil)))) (b* ((c (car y)) (d (cdr y)) ((when (or (aig-negation-p a c) (aig-negation-p a d) (aig-negation-p b c) (aig-negation-p b d))) (mv :subterm y y))) (mv :fail x y)))) (mv :fail x y)) /// (defret aig-and-pass4a-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil) (defret aig-and-pass4a-never-reduced (not (equal status :reduced))) (defret aig-and-pass4a-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-pass4a-normalize-status (implies (and (not (equal status :subterm)) (not (equal status :reduced))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))))
other
(define aig-and-pass4 (x y) :short "Level 2, Subsumption Rules 1 and 2, Both Directions." :returns (mv status arg1 arg2) :inline t (b* (((mv status arg1 arg2) (aig-and-pass4a y x)) ((unless (eq status :fail)) (mv status arg1 arg2))) (aig-and-pass4a x y)) /// (defret aig-and-pass4-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil :hints (("Goal" :use ((:instance aig-and-pass4a-correct) (:instance aig-and-pass4a-correct (x y) (y x)))))) (defret aig-and-pass4-never-reduced (not (equal status :reduced))) (defret aig-and-pass4-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-pass4-normalize-status (implies (and (not (equal status :subterm)) (not (equal status :reduced))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))))
other
(define aig-and-pass5 (x y) :short "Level 2, Resolution Rule." :returns (mv status arg1 arg2) :inline t (b* (((unless (and (not (aig-atom-p x)) (eq (cdr x) nil) (not (aig-atom-p (car x))) (not (eq (cdar x) nil)))) (mv :fail x y)) ((unless (and (not (aig-atom-p y)) (eq (cdr y) nil) (not (aig-atom-p (car y))) (not (eq (cdar y) nil)))) (mv :fail x y)) (a (caar x)) (b (cdar x)) (c (caar y)) (d (cdar y)) ((when (or (and (hons-equal a d) (aig-negation-p b c)) (and (hons-equal a c) (aig-negation-p b d)))) (let ((ans (aig-not a))) (mv :subterm ans ans))) ((when (or (and (hons-equal b d) (aig-negation-p a c)) (and (hons-equal b c) (aig-negation-p a d)))) (let ((ans (aig-not b))) (mv :subterm ans ans)))) (mv :fail x y)) /// (defret aig-and-pass5-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil) (defret aig-and-pass5-never-reduced (not (equal status :reduced))) (defret aig-and-pass5-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-pass5-normalize-status (implies (and (not (equal status :subterm)) (not (equal status :reduced))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))))
other
(define aig-and-pass6a (x y) :short "Level 3 Substitution Rules, Single Direction." :returns (mv status arg1 arg2) (b* (((unless (and (not (aig-atom-p x)) (eq (cdr x) nil) (not (aig-atom-p (car x))) (not (eq (cdar x) nil)))) (mv :fail x y)) (a (caar x)) (b (cdar x)) ((when (hons-equal a y)) (mv :reduced a (aig-not b))) ((when (hons-equal b y)) (mv :reduced b (aig-not a))) ((unless (and (not (aig-atom-p y)) (not (eq (cdr y) nil)))) (mv :fail x y)) (c (car y)) (d (cdr y)) ((when (or (hons-equal b c) (hons-equal b d))) (mv :reduced (aig-not a) y)) ((when (or (hons-equal a c) (hons-equal a d))) (mv :reduced (aig-not b) y))) (mv :fail x y)) /// (defret aig-and-pass6a-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil) (defret aig-and-pass6a-reduces-count (implies (eq status :reduced) (< (+ (aig-and-count arg1) (aig-and-count arg2)) (+ (aig-and-count x) (aig-and-count y)))) :rule-classes nil :hints (("Goal" :in-theory (enable aig-and-count)))) (defret aig-and-pass6a-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-pass6a-arg2-on-failure (implies (and (equal status :fail) y) (iff arg2 t))) (defret aig-and-pass6a-when-fail (implies (and (not (equal status :subterm)) (not (equal status :reduced))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))))
other
(define aig-and-pass6 (x y) :short "Level 3 Substitution Rules, Both Directions." :returns (mv status arg1 arg2) :inline t (b* (((mv status arg1 arg2) (aig-and-pass6a y x)) ((unless (eq status :fail)) (mv status arg1 arg2))) (aig-and-pass6a x y)) /// (defret aig-and-pass6-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil :hints (("Goal" :use ((:instance aig-and-pass6a-correct) (:instance aig-and-pass6a-correct (x y) (y x)))))) (defret aig-and-pass6-reduces-count (implies (eq status :reduced) (< (+ (aig-and-count arg1) (aig-and-count arg2)) (+ (aig-and-count x) (aig-and-count y)))) :rule-classes nil :hints (("Goal" :use ((:instance aig-and-pass6a-reduces-count) (:instance aig-and-pass6a-reduces-count (x y) (y x)))))) (defret aig-and-pass6-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-pass6-arg2-on-failure (implies (and (equal status :fail) y) (iff arg2 t))) (defret aig-and-pass6-when-fail (implies (and (not (equal status :subterm)) (not (equal status :reduced))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))))
other
(define aig-and-main (x y) :short "And-Node, Main Optimizations, Non-Recursive." :returns (mv status arg1 arg2) (b* (((mv hit ans) (aig-and-pass1 x y)) ((when hit) (mv :subterm ans ans)) ((mv status arg1 arg2) (aig-and-pass2 x y)) ((unless (eq status :fail)) (mv status arg1 arg2)) ((mv status arg1 arg2) (aig-and-pass3 x y)) ((unless (eq status :fail)) (mv status arg1 arg2)) ((mv status arg1 arg2) (aig-and-pass4 x y)) ((unless (eq status :fail)) (mv status arg1 arg2)) ((mv status arg1 arg2) (aig-and-pass5 x y)) ((unless (eq status :fail)) (mv status arg1 arg2))) (aig-and-pass6 x y)) /// (defret aig-and-main-correct (equal (and (aig-eval arg1 env) (aig-eval arg2 env)) (and (aig-eval x env) (aig-eval y env))) :rule-classes nil :hints (("Goal" :use ((:instance aig-and-pass1-correct) (:instance aig-and-pass2-correct) (:instance aig-and-pass3-correct) (:instance aig-and-pass4-correct) (:instance aig-and-pass5-correct) (:instance aig-and-pass6-correct))))) (defret aig-and-main-reduces-count (implies (eq status :reduced) (< (+ (aig-and-count arg1) (aig-and-count arg2)) (+ (aig-and-count x) (aig-and-count y)))) :rule-classes nil :hints (("Goal" :use ((:instance aig-and-pass3-reduces-count) (:instance aig-and-pass6-reduces-count))))) (defret aig-and-main-subterm-convention (implies (equal status :subterm) (equal arg2 arg1))) (defret aig-and-main-on-failure (implies (and (not (equal status :reduced)) (not (equal status :subterm))) (and (equal status :fail) (equal arg1 x) (equal arg2 y)))) (defthm aig-and-main-of-constants (and (equal (aig-and-main x nil) (mv :subterm nil nil)) (equal (aig-and-main nil y) (mv :subterm nil nil)) (equal (aig-and-main x t) (mv :subterm x x)) (equal (aig-and-main t x) (mv :subterm x x))) :hints (("Goal" :in-theory (enable aig-and-main aig-and-pass1)))) (defret aig-and-main-arg2-on-failure (implies (equal status :fail) arg2) :hints (("Goal" :in-theory (enable aig-and-pass1)))))
other
(define aig-binary-and (x y) :short "@(call aig-binary-and) constructs an AIG representing @('(and x y)')." :measure (+ (aig-and-count x) (aig-and-count y)) (b* (((mv status arg1 arg2) (aig-and-main x y)) ((when (eq status :subterm)) arg1) ((when (eq status :reduced)) (aig-binary-and arg1 arg2))) (hons arg1 arg2)) :hints (("Goal" :use ((:instance aig-and-main-reduces-count)))) /// (local (in-theory (enable aig-binary-and))) (defthm aig-and-constants (and (equal (aig-binary-and nil x) nil) (equal (aig-binary-and x nil) nil) (equal (aig-binary-and x t) x) (equal (aig-binary-and t x) x))) (local (in-theory (enable aig-atom-p-of-cons-strong))) (defthm aig-eval-and (equal (aig-eval (aig-binary-and x y) env) (and (aig-eval x env) (aig-eval y env))) :hints (("Goal" :induct (aig-binary-and x y) :do-not '(generalize fertilize)) (and stable-under-simplificationp '(:use ((:instance aig-and-main-correct)))))))
other
(define aig-and-macro-logic-part (args) :short "Generates the :logic part for a aig-and MBE call." :mode :program (cond ((atom args) t) ((atom (cdr args)) (car args)) (t `(aig-binary-and ,(CAR ARGS) ,(AIG-AND-MACRO-LOGIC-PART (CDR ARGS))))))
other
(define aig-and-macro-exec-part (args) :short "Generates the :exec part for a aig-and MBE call." :mode :program (cond ((atom args) t) ((atom (cdr args)) (car args)) (t `(let ((aig-and-x-do-not-use-elsewhere ,(CAR ARGS))) (and aig-and-x-do-not-use-elsewhere (aig-binary-and aig-and-x-do-not-use-elsewhere ,(AIG-AND-MACRO-EXEC-PART (CDR ARGS))))))))
other
(defsection aig-and :parents (aig-constructors) :short "@('(aig-and x1 x2 ...)') constructs an AIG representing @('(and x1 x2 ...)')." :long "<p>The main function is @(see aig-binary-and). It implements something like the algorithm described in:</p> <ul> <li>Robert Brummayer and Armin Biere. <a href='http://fmv.jku.at/papers/BrummayerBiere-MEMICS06.pdf'>Local Two-Level And Inverter Graph Minimization Without Blowup</a>. Mathematical and Engineering Methods in Computer Science (MEMICS). 2006.</li> </ul> <p>In particular, see Table 2 in that paper, which describes optimization rules that are ``locally size decreasing without affecting global sharing negatively.''</p> <p>We try to implement these rules in @(see aig-and-main), which returns:</p> @({ (mv status arg1 arg2) }) <p>The status is either:</p> <ul> <li>@(':fail') if no rule applies, in which case @('arg1') and @('arg2') are just copies of @('x') and @('y') and we need to construct a new AND node that joins them together;</li> <li>@(':subterm') if a rewrite rule applies that reduces the AND of @('x') and @('y') to either a constant or to a subterm of @('x') or @('y'). This subterm is returned as both @('arg1') and @('arg2'). In this case, we assume there is no more rewriting to be done and just return the reduced subterm.</li> <li>@(':reduced') if a rewrite rule applies that reduces the AND in some interesting way, where it is no longer a proper subterm of @('x') or @('y'). In this case, it may be possible to further reduce @('arg1') and @('arg2'), so we want to recursively rewrite them.</li> </ul> <p>@('aig-and') itself is a macro which extends @('aig-binary-and') across many arguments. As one last special optimization, when there is more than one argument we try to "short-circuit" the computation and avoid evaluating some arguments.</p> <p>See also @(see aig-and-dumb), which is much less sophisticated but may be easier to reason about in certain cases where you really care about the structure of the resulting AIGs.</p> <p>A June 2015 experiment suggests that, for a particular 80-bit floating point addition problem, this fancier algorithm improves the size of AIGs produced by @(see SV) by about 3% when measured either by unique AND nodes or by unique conses.</p> @(def aig-and)" (defmacro aig-and (&rest args) `(mbe :logic ,(AIG-AND-MACRO-LOGIC-PART ARGS) :exec ,(AIG-AND-MACRO-EXEC-PART ARGS))) (add-binop aig-and aig-binary-and) (local (defthm aig-and-sanity-check (and (equal (aig-and) t) (equal (aig-and x) x) (equal (aig-and x y) (aig-binary-and x y)) (equal (aig-and x y z) (aig-binary-and x (aig-binary-and y z)))) :rule-classes nil)))
local
(local (set-default-parents))
other
(define aig-binary-or (x y) :parents (aig-or) :short "@(call aig-binary-or) constructs an AIG representing @('(or x y)')." :returns aig (cond ((eq x nil) y) ((eq y nil) x) (t (aig-not (aig-and (aig-not x) (aig-not y))))) /// (defthm aig-eval-or (equal (aig-eval (aig-binary-or x y) env) (or (aig-eval x env) (aig-eval y env)))) (defthm aig-or-constants (and (equal (aig-binary-or nil x) x) (equal (aig-binary-or x nil) x) (equal (aig-binary-or x t) t) (equal (aig-binary-or t x) t))))
other
(define aig-or-macro-logic-part (args) :parents (aig-or) :mode :program (cond ((atom args) nil) ((atom (cdr args)) (car args)) (t `(aig-binary-or ,(CAR ARGS) ,(AIG-OR-MACRO-LOGIC-PART (CDR ARGS))))))
other
(define aig-or-macro-exec-part (args) :parents (aig-or) :mode :program (cond ((atom args) nil) ((atom (cdr args)) (car args)) (t `(let ((aig-or-x-do-not-use-elsewhere ,(CAR ARGS))) (if (eq t aig-or-x-do-not-use-elsewhere) t (aig-binary-or aig-or-x-do-not-use-elsewhere (check-vars-not-free (aig-or-x-do-not-use-elsewhere) ,(AIG-OR-MACRO-EXEC-PART (CDR ARGS)))))))))
other
(defsection aig-or :parents (aig-constructors) :short "@('(aig-or x1 x2 ...)') constructs an AIG representing @('(or x1 x2 ...)')." :long "<p>Like @(see aig-and), we attempt to lazily avoid computing later terms in the expression.</p> @(def aig-or)" (defmacro aig-or (&rest args) `(mbe :logic ,(AIG-OR-MACRO-LOGIC-PART ARGS) :exec ,(AIG-OR-MACRO-EXEC-PART ARGS))) (add-binop aig-or aig-binary-or) (local (defthm aig-or-sanity-check (or (equal (aig-or) nil) (equal (aig-or x) x) (equal (aig-or x y) (aig-binary-or x y)) (equal (aig-or x y z) (aig-binary-or x (aig-binary-or y z)))) :rule-classes nil)))
other
(define aig-xor (x y) :parents (aig-constructors) :short "@(call aig-xor) constructs an AIG representing @('(xor x y)')." :returns aig (aig-or (aig-and x (aig-not y)) (aig-and y (aig-not x))) /// (defthm aig-eval-xor (equal (aig-eval (aig-xor x y) env) (xor (aig-eval x env) (aig-eval y env)))))
other
(define aig-iff (x y) :parents (aig-constructors) :short "@(call aig-iff) constructs an AIG representing @('(iff x y)')." :returns aig (aig-or (aig-and x y) (aig-and (aig-not x) (aig-not y))) /// (defthm aig-eval-iff (equal (aig-eval (aig-iff x y) env) (iff (aig-eval x env) (aig-eval y env)))))
other
(define aig-implies-fn (x y) :parents (aig-implies) :returns aig (aig-not (aig-and x (aig-not y))) /// (defthm aig-eval-implies (equal (aig-eval (aig-implies-fn x y) env) (implies (aig-eval x env) (aig-eval y env)))) (defthm aig-eval-implies-nil (equal (aig-implies-fn nil x) t)))
other
(defsection aig-implies :parents (aig-constructors) :short "@(call aig-implies) constructs an AIG representing @('(implies x y)')." :long "<p>We try to lazily avoid evaluating @('y').</p> @(def aig-implies)" (defmacro aig-implies (x y) `(mbe :logic (aig-implies-fn ,X ,Y) :exec (let ((aig-implies-x-do-not-use-elsewhere ,X)) (if (eq nil aig-implies-x-do-not-use-elsewhere) t (aig-implies-fn aig-implies-x-do-not-use-elsewhere (check-vars-not-free (aig-implies-x-do-not-use-elsewhere) ,Y)))))) (add-macro-alias aig-implies aig-implies-fn) (local (defthm aig-implies-sanity-check (equal (aig-implies x y) (aig-implies-fn x y)) :rule-classes nil)))
other
(defsection aig-nand :parents (aig-constructors) :short "@(call aig-nand) constructs an AIG representing @('(not (and x y))')." :long "@(def aig-nand)" (defmacro aig-nand (x y) `(aig-not (aig-and ,X ,Y))))
other
(defsection aig-nor :parents (aig-constructors) :short "@(call aig-nor) constructs an AIG representing @('(not (or x y))')." (defmacro aig-nor (x y) `(aig-and (aig-not ,X) (aig-not ,Y))))
other
(defsection aig-andc1 :parents (aig-constructors) :short "@(call aig-andc1) constructs an AIG representing @('(and (not x) y)')." (defmacro aig-andc1 (x y) `(aig-and (aig-not ,X) ,Y)))
other
(defsection aig-andc2 :parents (aig-constructors) :short "@(call aig-andc2) constructs an AIG representing @('(and x (not y))')." (defmacro aig-andc2 (x y) `(aig-and ,X (aig-not ,Y))))
other
(defsection aig-orc1 :parents (aig-constructors) :short "@(call aig-orc1) is identical to @(see aig-implies)." :long "@(def aig-orc1)" (defmacro aig-orc1 (x y) `(aig-implies ,X ,Y)))
other
(defsection aig-orc2 :parents (aig-constructors) :short "@(call aig-orc2) constructs an AIG representing @('(or x (not y))')." (defmacro aig-orc2 (x y) `(aig-not (aig-and (aig-not ,X) ,Y))))
other
(define aig-ite-fn (a b c) :parents (aig-ite) :returns aig (cond ((eq a t) b) ((eq a nil) c) ((hons-equal b c) b) ((eq b t) (aig-or a c)) (t (aig-or (aig-and a b) (aig-and (aig-not a) c)))) /// (defthm aig-eval-ite (iff (aig-eval (aig-ite-fn a b c) env) (if (aig-eval a env) (aig-eval b env) (aig-eval c env)))) (defthm aig-ite-of-constants (and (equal (aig-ite-fn t b c) b) (equal (aig-ite-fn nil b c) c))))
other
(defsection aig-ite :parents (aig-constructors) :short "@(call aig-ite) constructs an AIG representing @('(if a b c)')." :long "<p>This is logically just @(see aig-ite-fn), but we try to lazily avoid computing @('b') or @('c') when the value of @('a') is known.</p>" (defmacro aig-ite (a b c) `(mbe :logic (aig-ite-fn ,A ,B ,C) :exec (let ((aig-ite-x-do-not-use-elsewhere ,A)) (cond ((eq aig-ite-x-do-not-use-elsewhere t) ,B) ((eq aig-ite-x-do-not-use-elsewhere nil) ,C) (t (aig-ite-fn aig-ite-x-do-not-use-elsewhere ,B ,C)))))) (add-macro-alias aig-ite aig-ite-fn))
other
(define aig-not-list (x) :parents (aig-constructors) :short "@(call aig-not-list) negates every AIG in the list @('x')." :returns aig-list :enabled t (if (atom x) nil (cons (aig-not (car x)) (aig-not-list (cdr x)))))
other
(define member-eql-without-truelistp ((a eqlablep) x) :parents (aig-and-list aig-or-list) :enabled t (mbe :logic (member a x) :exec (cond ((atom x) nil) ((eql a (car x)) x) (t (member-eql-without-truelistp a (cdr x))))))
other
(define aig-and-list-aux (x) :enabled t :parents (aig-and-list) (if (atom x) t (aig-and (car x) (aig-and-list-aux (cdr x)))))
other
(define aig-and-list (x) :parents (aig-constructors) :short "@(call aig-and-list) <i>and</i>s together all of the AIGs in the list @('x')." :long "<p>As a dumb attempt at optimization, we try to avoid consing if we see that there's a @('nil') anywhere in the list. This won't win very often, but it is quite cheap and it can win big when it does win by avoiding a lot of AIG construction.</p>" :returns aig :enabled t :verify-guards nil (mbe :logic (if (atom x) t (aig-and (car x) (aig-and-list (cdr x)))) :exec (cond ((atom x) t) ((member-eql-without-truelistp nil x) nil) (t (aig-and-list-aux x)))) /// (defthm aig-and-list-aux-removal (equal (aig-and-list-aux x) (aig-and-list x))) (local (defthm aig-and-list-when-member-nil (implies (member nil x) (equal (aig-and-list x) nil)))) (verify-guards aig-and-list))
other
(define aig-or-list-aux (x) :enabled t :parents (aig-or-list) (if (atom x) nil (aig-or (car x) (aig-or-list-aux (cdr x)))))
other
(define aig-or-list (x) :parents (aig-constructors) :short "@(call aig-or-list) <i>or</i>s together all of the AIGs in the list @('x')." :long "<p>As a dumb attempt at optimization, we try to avoid consing if we see that there's a @('t') anywhere in the list. This won't win very often, but it is quite cheap and it can win big when it does win by avoiding a lot of AIG construction.</p>" :returns aig :enabled t :verify-guards nil (mbe :logic (if (atom x) nil (aig-or (car x) (aig-or-list (cdr x)))) :exec (cond ((atom x) nil) ((member-eql-without-truelistp t x) t) (t (aig-or-list-aux x)))) /// (defthm aig-or-list-aux-removal (equal (aig-or-list-aux x) (aig-or-list x))) (local (defthm l0 (and (equal (aig-or t y) t) (equal (aig-or y t) t)) :hints (("Goal" :in-theory (enable aig-or))))) (local (defthm aig-or-list-when-member-t (implies (member t x) (equal (aig-or-list x) t)))) (verify-guards aig-or-list))
other
(define aig-and-lists (x y) :parents (aig-constructors) :short "@(call aig-and-lists) pairwise <i>and</i>s together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-binary-and (car x) (car y)) (aig-and-lists (cdr x) (cdr y)))))
other
(define aig-or-lists (x y) :parents (aig-constructors) :short "@(call aig-or-lists) pairwise <i>or</i>s together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-binary-or (car x) (car y)) (aig-or-lists (cdr x) (cdr y)))))
other
(define aig-xor-lists (x y) :parents (aig-constructors) :short "@(call aig-xor-lists) pairwise <i>xor</i>s together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-xor (car x) (car y)) (aig-xor-lists (cdr x) (cdr y)))))
other
(define aig-iff-lists (x y) :parents (aig-constructors) :short "@(call aig-iff-lists) pairwise <i>iff</i>s together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-iff (car x) (car y)) (aig-iff-lists (cdr x) (cdr y)))))
other
(define aig-implies-lists (x y) :parents (aig-constructors) :short "@(call aig-implies-lists) pairwise <i>implies</i> together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-implies-fn (car x) (car y)) (aig-implies-lists (cdr x) (cdr y)))))
other
(define aig-nand-lists (x y) :parents (aig-constructors) :short "@(call aig-nand-lists) pairwise <i>nand</i>s together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-nand (car x) (car y)) (aig-nand-lists (cdr x) (cdr y)))))
other
(define aig-nor-lists (x y) :parents (aig-constructors) :short "@(call aig-nor-lists) pairwise <i>nor</i>s together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-nor (car x) (car y)) (aig-nor-lists (cdr x) (cdr y)))))
other
(define aig-andc1-lists (x y) :parents (aig-constructors) :short "@(call aig-andc1-lists) pairwise <i>andc1</i>s together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-andc1 (car x) (car y)) (aig-andc1-lists (cdr x) (cdr y)))))
other
(define aig-andc2-lists (x y) :parents (aig-constructors) :short "@(call aig-andc2-lists) pairwise <i>andc2</i>s together the AIGs from the lists @('x') and @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-andc2 (car x) (car y)) (aig-andc2-lists (cdr x) (cdr y)))))
other
(defsection aig-orc1-lists :parents (aig-constructors) :short "@(call aig-orc1-lists) is identical to @(see aig-implies-lists)." :long "@(def aig-orc1-lists)" (defmacro aig-orc1-lists (x y) `(aig-implies-lists ,X ,Y)))
other
(define aig-orc2-lists (x y) :parents (aig-constructors) :short "@(call aig-orc2-lists) pairwise <i>orc2</i>s together the AIGs from the lists @('x') or @('y')." :returns aig-list :enabled t (if (or (atom x) (atom y)) nil (cons (aig-orc2 (car x) (car y)) (aig-orc2-lists (cdr x) (cdr y)))))
other
(def-ruleset aig-constructors
'(aig-not aig-and
aig-or
aig-xor
aig-iff
aig-implies
aig-ite
aig-not-list
aig-and-list
aig-or-list
aig-and-lists
aig-or-lists
aig-xor-lists
aig-iff-lists
aig-nand-lists
aig-nor-lists
aig-implies-lists
aig-andc1-lists
aig-andc2-lists
aig-orc2-lists))
other
(define aig-restrict :parents (aig-substitution) :short "@(call aig-restrict) performs variable substitution throughout the AIG @('x'), replacing any variables bound in @('sigma') with their corresponding values." ((x "The AIG to restrict.") (sigma "A fast alist binding variables to replacement AIGs.")) :returns (aig "Modified version of @('x') where all variables bound in @('sigma') are replaced, and any unmentioned variables are left <b>unchanged</b>.") :long "<p>The name @('sigma') is intended to evoke the notion of substitution lists in logic. Any variables that are not mentioned in @('sigma') are left unchanged. When all of the variables in @('x') are bound in @('sigma'), and all of the values are Boolean, this is equivalent to @(see aig-eval).</p> <p>This function is @(see memoize)d. You should typically free its memo table after you are done with whatever @('sigma') you are using, to avoid excessive memory usage. (We don't use @(':forget t') because you often want to restrict several related AIGs.)</p>" :enabled t (aig-cases x :true t :false nil :var (let ((a (hons-get x sigma))) (if a (cdr a) x)) :inv (aig-not (aig-restrict (car x) sigma)) :and (let ((a (aig-restrict (car x) sigma))) (and a (aig-and a (aig-restrict (cdr x) sigma))))) /// (memoize 'aig-restrict :condition '(and (consp x) (cdr x))) (local (defthm hons-assoc-equal-of-append (equal (hons-assoc-equal k (append a b)) (or (hons-assoc-equal k a) (hons-assoc-equal k b))))) (defthm aig-eval-of-aig-restrict (equal (aig-eval (aig-restrict x al1) al2) (aig-eval x (append (aig-eval-alist al1 al2) al2))) :hints (("Goal" :induct t :in-theory (enable aig-env-lookup)))))
other
(define aig-restrict-list-acc (x sigma (acc true-listp)) (if (atom x) (revappend acc nil) (aig-restrict-list-acc (cdr x) sigma (cons (aig-restrict (car x) sigma) acc))))
other
(define aig-restrict-list :parents (aig-substitution) :short "@(call aig-restrict-list) substitutes into a list of AIGs." ((x "List of AIGs.") (sigma "Fast alist binding variables to replacement AIGs, as in @(see aig-restrict).")) :returns aig-list :enabled t :verify-guards nil (mbe :logic (if (atom x) nil (cons (aig-restrict (car x) sigma) (aig-restrict-list (cdr x) sigma))) :exec (aig-restrict-list-acc x sigma nil)) /// (local (defthm aig-restrict-list-acc-elim (equal (aig-restrict-list-acc x sigma acc) (revappend acc (aig-restrict-list x sigma))) :hints (("Goal" :in-theory (enable aig-restrict-list-acc))))) (verify-guards aig-restrict-list))
other
(define aig-restrict-alist :parents (aig-substitution) :short "@(call aig-restrict-alist) substitutes into an AIG Alist (an alist binding keys to AIGs)." ((x "Alist binding names to AIGs. This doesn't need to be a fast alist.") (sigma "Fast alist binding variables to replacement AIGs, as in @(see aig-restrict).")) :returns (aig-alist "Ordinary (slow) alist with the same keys as @('x'), and values formed by restricting each aig with @(see aig-restrict).") :enabled t (cond ((atom x) nil) ((atom (car x)) (aig-restrict-alist (cdr x) sigma)) (t (cons (cons (caar x) (aig-restrict (cdar x) sigma)) (aig-restrict-alist (cdr x) sigma)))) /// (defthm alistp-of-aig-restrict-alist (alistp (aig-restrict-alist x sigma))))
other
(define aig-restrict-alists :parents (aig-substitution) :short "@(call aig-restrict-alists) substitutes into a list of AIG Alists." ((x "List of AIG alists, which need not be fast.") (sigma "Fast alist binding variables to replacement AIGs, as in @(see aig-restrict).")) :returns (aig-alists "List of ordinary (slow) alists, derived from @('x') via @(see aig-restrict-alist).") :enabled t (if (atom x) nil (cons (aig-restrict-alist (car x) sigma) (aig-restrict-alists (cdr x) sigma))))
other
(define aig-compose :parents (aig-substitution) :short "@(call aig-compose) performs variable substitution throughout the AIG @('x'), <b>unconditionally</b> replacing every variable in @('x') with its binding in @('sigma')." ((x "The AIG to compose into.") (sigma "A fast alist that should bind variables to replacement AIGs.")) :returns (aig "Modified version of @('x') where every variable is replaced with its binding in @('sigma') or @('t') if it has no binding.") :long "<p>The name @('sigma') is intended to evoke the notion of substitution lists in logic. This operation is similar to @(see aig-restrict), except that whereas @('aig-restrict') leaves unbound variables alone, @('aig-compose') replaces them with @('t'). This has the logically nice property that the variables after composition are just the variables in the AIGs of @('sigma').</p> <p>This function is @(see memoize)d. You should typically free its memo table after you are done with whatever @('sigma') you are using, to avoid excessive memory usage. (We don't use @(':forget t') because you often want to compose several related AIGs.)</p>" :enabled t (aig-cases x :true t :false nil :var (aig-alist-lookup x sigma) :inv (aig-not (aig-compose (car x) sigma)) :and (let ((a (aig-compose (car x) sigma))) (and a (aig-and a (aig-compose (cdr x) sigma))))) /// (memoize 'aig-compose :condition '(and (consp x) (cdr x))))
other
(define aig-compose-list :parents (aig-substitution) :short "@(call aig-compose-list) composes into a list of AIGs." ((x "List of AIGs.") (sigma "Fast alist binding variables to replacement AIGs, as in @(see aig-compose).")) :returns aig-list :enabled t (if (atom x) nil (cons (aig-compose (car x) sigma) (aig-compose-list (cdr x) sigma))))
other
(define aig-compose-alist :parents (aig-substitution) :short "@(call aig-compose-alist) composes into an AIG Alist (an alist binding keys to AIGs)." ((x "Alist binding names to AIGs. This doesn't need to be a fast alist.") (sigma "Fast alist binding variables to replacement AIGs, as in @(see aig-compose).")) :returns (aig-alist "Ordinary (slow) alist with the same keys as @('x'), and values formed by restricting each aig with @(see aig-compose).") :enabled t (cond ((atom x) nil) ((atom (car x)) (aig-compose-alist (cdr x) sigma)) (t (cons (cons (caar x) (aig-compose (cdar x) sigma)) (aig-compose-alist (cdr x) sigma)))))
other
(define aig-compose-alists :parents (aig-substitution) :short "@(call aig-compose-alists) composes into a list of AIG Alists." ((x "List of AIG alists, which need not be fast.") (sigma "Fast alist binding variables to replacement AIGs, as in @(see aig-compose).")) :returns (aig-alists "List of ordinary (slow) alists, derived from @('x') via @(see aig-compose-alist).") :enabled t (if (atom x) nil (cons (aig-compose-alist (car x) sigma) (aig-compose-alists (cdr x) sigma))))
other
(define aig-partial-eval :parents (aig-substitution) :short "@(call aig-partial-eval) evaluates @('x'), an AIG, under the partial environment @('env'), producing a new AIG as a result." ((x "The AIG to partially evaluate.") (env "A fast alist that (typically) binds some of the variables in @('x') to Boolean values.")) :returns (aig "Modified version of @('x') obtained by replacing bound variables with their values and doing basic constant propagation.") :long "<p>In ordinary AIG evaluation with @(see aig-eval), any variables that are missing from @('env') are just assumed to have a default value. Because of this, every variable can be given a Boolean value and we can evaluate the whole AIG to produce a Boolean result.</p> <p>In partial evaluation, variables that aren't bound in @('env') are left alone. Because of this, the result of a partial evaluation is a typically a reduced AIG instead of a Boolean.</p> <p>Another way to do partial evaluations is with @(see aig-restrict). In fact, the only difference between @('aig-restrict') and @('aig-partial-eval') is that @('aig-partial-eval') Boolean-fixes the values in the alist as it looks them up. This has logically nice properties, e.g., since we never replace a variable by a subtree, only by a Boolean, we know unconditionally that the variables of the resulting AIG are a subset of the variables of the original.</p> <p>This function is @(see memoize)d. You should typically free its memo table after you are done with whatever @('env') you are using, to avoid excessive memory usage. (We don't use @(':forget t') because you often want to evaluate several related AIGs.)</p>" :enabled t (aig-cases x :true t :false nil :var (let ((a (hons-get x env))) (if a (and (cdr a) t) x)) :inv (aig-not (aig-partial-eval (car x) env)) :and (let ((a (aig-partial-eval (car x) env))) (and a (aig-and a (aig-partial-eval (cdr x) env))))) /// (memoize 'aig-partial-eval :condition '(and (consp x) (cdr x))))
other
(define aig-partial-eval-list :parents (aig-substitution) :short "@(call aig-partial-eval-list) partially evaluates a list of AIGs." ((x "List of AIGs.") (env "Fast alist binding variables to Booleans, as in @(see aig-partial-eval).")) :returns aig-list :enabled t (if (atom x) nil (cons (aig-partial-eval (car x) env) (aig-partial-eval-list (cdr x) env))))
other
(define aig-partial-eval-alist :parents (aig-substitution) :short "@(call aig-partial-eval-alist) partially evaluates an AIG Alist (an alist binding keys to AIGs)." ((x "Alist binding names to AIGs. This doesn't need to be a fast alist.") (env "Fast alist binding variables to Booleans, as in @(see aig-partial-eval).")) :returns (aig-alist "Ordinary (slow) alist with the same keys as x, and values formed by restricting each aig with @(see aig-partial-eval).") :enabled t (cond ((atom x) nil) ((atom (car x)) (aig-partial-eval-alist (cdr x) env)) (t (cons (cons (caar x) (aig-partial-eval (cdar x) env)) (aig-partial-eval-alist (cdr x) env)))) /// (defthm alistp-of-aig-partial-eval-alist (alistp (aig-partial-eval-alist x env))))
other
(define aig-env-extract (vars env) :returns (extract true-listp :rule-classes :type-prescription) (if (atom vars) nil (hons-acons (car vars) (aig-env-lookup (car vars) env) (aig-env-extract (cdr vars) env))) /// (defthm aig-env-lookup-in-aig-env-extract (iff (aig-env-lookup v (aig-env-extract vars env)) (if (member v vars) (aig-env-lookup v env) t))) (defthm hons-assoc-equal-in-aig-env-extract (equal (hons-assoc-equal v (aig-env-extract vars env)) (and (member v vars) (cons v (aig-env-lookup v env))))))