make a rule to forward chain when a certain trigger arises
Major Section:  RULE-CLASSES


                                  known assumptions

General Form:
Any theorem, provided an acceptable triggering term exists.



:Forward-chaining rules are used by the simplifier before it begins to rewrite the literals of the goal. If any term in the goal is an instance of a trigger of some forward chaining rule, we try to establish the hypotheses of that forward chaining theorem (from the negation of the goal). To relieve a hypotheses we only use type reasoning, evaluation of ground terms, and presence among our known assumptions. We do not use rewriting. If all hypotheses are relieved, we add the instantiated conclusion to our known assumptions.


make a rule to restrict generalizations
Major Section:  RULE-CLASSES

See rule-classes for a general discussion of rule classes and how they are used to build rules from formulas. An example :corollary formula from which a :generalize rule might be built is:

any theorem

General Form: any theorem

To use such a :generalize rule, the system waits until it has decided to generalize some term, term, by replacing it with some new variable v. If any :generalize formula can be instantiated so that some non-variable subterm becomes term, then that instance of the formula is added as a hypothesis.

At the moment, the best description of how ACL2 :generalize rules are used may be found in the discussion of ``Generalize Rules,'' pp 248 of A Computational Logic Handbook.


make a rule that suggests a certain induction
Major Section:  RULE-CLASSES

            :pattern (* 1/2 i)
            :scheme (recursion-by-sub2 i))






General Form of an :induction Lemma or Corollary:




Consider, for example, the example given above,

(:induction :pattern (* 1/2 i)
            :scheme (recursion-by-sub2 i)).
(defun recursion-by-sub2 (i)
  (if (and (integerp i)
           (< 1 i))
      (recursion-by-sub2 (- i 2))
              (:p i))
                   (:p (- i 2)))
              (:p i)))




(defthm name T
                             :condition cond-term
                             :scheme scheme-term)))


make some arithmetic inequality rules
Major Section:  RULE-CLASSES


         (>= (length x)              compares to any other term, add to
             (length (member e x)))) set of known inequalities the fact
                                     that it is no larger than (length b),
                                     provided (eqlablep a) and (true-listp b)
                                     rewrite to t




:linear rules are used by a linear arithmetic decision procedure during rewriting. We describe the procedure very roughly here. Fundamental to the procedure is the notion of a linear polynomial inequality. A ``linear polynomial'' is a sum of terms, each of which is the product of a rational constant and an ``unknown.'' The ``unknown'' is permitted to be 1 simply to allow a term in the sum to be constant. Thus, an example linear polynomial is 3*x + 7*a + 2; here x and a are the (interesting) unknowns. In our case, the unknowns need not be variable symbols. For example, (length x) might be used as an unknown in a linear polynomial. Thus, another linear polynomial is 3*(length x) + 7*a. A ``linear polynomial inequality'' is an inequality (either < or <=) relation between two linear polynomials. Certain linear polynomial inequalities can be combined by cross-multiplication and addition to permit the deduction of a third linear inequality polynomial with fewer unknowns. If this deduced inequality is manifestly false, a contradiction has been deduced from the assumed inequalities.

For example, suppose we have three assumptions:

p1:  3*x + 7*a <  4 
p2:          3 <  2*x
p3:          0 <= a.
By cross-multiplying and adding the first two (that is, multiplying p1 by 2, p2 by 3 and adding the respective sides), we deduce the intermediate result
which, after cancellation, is
p4:        14*a + 1 <  0.
p5:               1 <= 0,

All of the unknowns of a polynomial must be eliminated by cancellation in order to produce a constant inequality. We can choose to eliminate the unknowns in any order. We eliminate them in term-order, largest unknowns first. (See term-order.) Now suppose that this procedure does not produce a contradiction but instead yields a set of nontrivial inequalities. A contradiction might still be deduced if we could add to the set some additional inequalities allowing additional cancellation. That is where :linear lemmas come in. When the set of inequalities has stabilized under cross-multiplication and addition and no contradiction is produced, we search the data base of :linear rules for rules about the unknowns that are candidates for cancellation (i.e., are the largest unknowns in their respective inequalities).

If the trigger term of some :linear rule can be instantiated to yield a candidate for cancellation, we so instantiate that rule, attempt to relieve the hypotheses with general-purpose rewriting, and, if successful, convert the concluding inequality into a polynomial inequality and add it to the set. This may let cancellation continue. See the discussion of ``Linear Arithmetic Rules'' pp 242 of A Computational Logic Handbook for more details.


General Form of a Linear Rule Class:
         :TRIGGER-TERMS (term1 ... termk))

Note: Problems may arise if you explicitly store a linear lemma under a trigger term that, when instantiated, is not the largest unknown in the instantiated concluding inequality. Suppose for example you store the linear rule (<= (fn i j) (/ i (* j j))) under the trigger term (fn i j). Then when the system ``needs'' an inequality about (fn a b), i.e., because (fn a b) is the largest unknown in some inequality in the set of assumed inequalities, it will appeal to the rule and deduce (<= (fn a b) (/ a (* b b))). However, the largest unknown in this inequality is (/ a (* b b)) and hence this inequality will not be used until that term is eliminated by cancellation with some other inequality. It is generally best to specify as a trigger term one of the ``maximal'' terms of the polynomial, as described below.


The ``addends'' of (+ x y) and (- x y) are the union of the addends of x and y. The addends of (- x) and (* n x), where n is a numeric constant, is in all cases just {x}. The addends of an inequality are the union of the addends of the left- and right-hand sides. The addends of any other term, x, is {x}.

A term is maximal for a conjunct (implies hyps concl) of the corollary if (a) the term is a non-variable, non-quote, non-lambda application, non-let and non-if expression, (b) the term contains enough variables so that when they are instantiated and the hypotheses are relieved then all the variables in concl are instantiated, and (c) any instantiation of the term is always at least as ``large'' (see term-order for a description of the order used) as the corresponding instantiations of any other addend in concl.


make a rule to extend the applicability of linear arithmetic
Major Section:  RULE-CLASSES

See rule-classes for a general discussion of rule classes and how they are used to build rules from formulas. An example :corollary formula from which a :linear-alias rule might be built is as follows. (Of course, the functions fix, plus, diff, and lessp would have to be defined first.)

(and (equal (fix (plus x y)) (plus x y))
     (equal (fix (diff x y)) (diff x y))
     (equal (fix (fix x)) (fix x))
     (equal (lessp x y) (< (fix x) (fix y)))
     (equal (plus x y) (+ (fix x) (fix y)))
     (implies (not (lessp x y))
              (equal (diff x y) (- (fix x) (fix y)))))
:linear-alias rules are essentially a hack to allow the creation of the "NQTHM" package. We will be surprised if the casual user of ACL2 ever finds a use for them. They are not documented very thoroughly.

General Forms:
(equal lhs rhs)
(implies hyps (equal lhs rhs))
where every variable that occurs in the rule must occur in the lhs. As for :rewrite rules, we strip the conjuncts out of the :corollary formula and generate one :linear-alias rule for each. Thus, it is possible to package together a complete set of :linear-alias rules under a single name.

When linear arithmetic is applied to a term that is not ``linearizable,'' i.e., a term that does not start with <, +, or -, it attempts to transduce the term to a linearizable one by applying all known :linear-alias rules as rewrite rules. This transduction is performed by the simple rewrite function rewrite-with-linear-aliases. Like the standard ACL2 rewriter, the linear alias rewriter applies linear alias rules exhaustively in the sense that if a rule applies then the rhs of the rule is recursively rewritten. Unlike :rewrite rules, :linear-alias rules are applied from outside in (!) and hypotheses are not relieved but merely collected.

For example, rewriting (lessp (diff (plus a b) c) (diff u v)) with the rules generated from the conjunction above produces

(< (+ (+ (fix a) (fix b))
      (- (fix c)))
   (+ (fix u) (- (fix v))))
under the hypothesis that (not (lessp (plus a b) c)) and (not (lessp u v)). (Actually, the + and - operators shown above are really calls of binary-+ and unary--.)

Think of :linear-alias rules as transforming a term from one theory to another, generating hypotheses in the first theory. Call the initial theory ``nqthm'' and the final one ``acl2.'' One should strive to create a set of rules that transform from the nqthm to the acl2 theory while only generating hypotheses in the first theory. The restriction is motivated by the desire not to surprise the nqthm user by suddently introducing conditions that are not phrased in the nqthm theory. The purpose of the nqthm package is to emulate nqthm, so it simply represents a failure if acl2 suddenly produces a case split, say, in which some non-nqthm term appears, such as (rationalp (plus a b)). To this end, we offer some advice below. However, it is especially helpful to study how the outside-in rewrite strategy uses the rules above to achieve this goal.

For example,

  (lessp (plus a b) (diff u v))
first steps to
  (< (fix (plus a b)) (fix (diff u v))),
unconditionally. But plus and diff don't need fixing:
  (< (plus a b) (diff u v)).
Rewriting further finishes with
  (< (+ (fix a) (fix b)) (+ (fix u) (- (fix v)))).
Observe that had we rewritten inside-out the sequence would have been:
and then
  (< (fix (+ (fix a) (fix b))) (fix  (+ (fix u) (- (fix v))))).
Note that we now need to know that + doesn't need fixing if its arguments have been. But that is a more complicated rule than the simple one that plus doesn't need fixing. Perhaps a different set of rules would work well for inside-out rewriting, but I just used the ones that first leapt to mind. It is interesting to note that linearization proceeds outside in too.

Here then is the advice. Rules should either transform nqthm terms to nqthm terms, e.g., (fix (plus x y)) to (plus x y), or should transform nqthm terms to acl2 terms, e.g., (lessp x y) to (< (fix x) (fix y)). When the rhs introduces an acl2 function symbol, the symbol should be outside of all nqthm terms in the rhs. Hypotheses should be expressed in the nqthm theory. No rule should rewrite an acl2 function symbol. If the transformation involves ``fixers'' then each operator should have two rules about it. The first, like (plus x y) = (+ (fix x) (fix y)), explains the nqthm function in terms of an acl2 function with fixed arguments. The second, like (fix (plus x y)) = (plus x y) eliminates the interior fixes generated by the first rule. These conventions mean that the rewriter, by going from outside in, always sees nqthm terms and instantiates the hypotheses of rules with nqthm terms. To violate these conventions might produce a set of rules that would cause a non-nqthm hypothesis to be generated.

Finally, observe that the rules probably will introduce a ``fixer'', which is the function fix in the NQTHM package. The fixer tends to settle around the nonlinear leaves of the resultant linearizable term. It is important to have a :type-prescription rule establishing that the fixer produces a rational.

When a :linear arithmetic rule is proved its conclusion is linearized to obtain the relevant polynomials. :linear-alias rules may kick in there; thus, a conclusion of the form (not (lessp (length str) (strpos pat str))) may be a legal :linear rule. Note however that its linearization is (not (< (fix (length str)) (fix (strpos pat str)))). The maximal term of this rule is (fix (strpos pat str)). However, we do not want to store such a linear rule under fix because that would result in all of the Nqthm proveall's linear rules being stored under the same symbol. We therefore note when the maximal term does not actually occur in the conclusion, is of the form (fix term), for some unary function fix, and term does occur in the conclusion. In that case, we call fix a ``fixer'' and we store a t under the property 'linear-alias-fixerp. We then store the rule with the maximal term term instead of (fix term). Later, when we are adding lemmas to the linear pot, we note that if we seek linear lemmas about (fix term), where fix is a fixer, we look for lemmas both stored under the function symbol of term and stored under the function symbol fix. In all cases, the maximal term of the rule contains as its top-most function symbol the symbol under which the rule is stored. Thus, if we seek lemmas about (fix (strpos pat str)) we will go to the property list of strpos and there find rules about (strpos pat str). We will instantiate their conclusions and linearize them, producing polynomials about (fix (strpos pat str)). This raises a subtle point. It could be that there is more than one fixer used in connection with strpos. Thus, some lemmas could linearize to polynomials about (rat (strpos pat str)). Therefore, when we create the linear rule with (strpos pat str) as its (fixed) maximal term, we store in the :fixer field of the rule the symbol fix to indicate that the linearization of this conclusion will (probably) produce (fix (strpos pat str)) terms. When looking for lemmas about (fix (strpos pat str)) we actually look for lemmas about strpos that have :fixer fix.

Here is an extended example of the use of linear aliasing. First, we define some nqthm-level functions:

(defun fix (x) (if (and (integerp x) (>= x 0)) x 0))
(defun lessp (x y) (< (fix x) (fix y)))
(defun plus (x y) (+ (fix x) (fix y)))
(defun diff (x y) (fix (- (fix x) (fix y))))
Note that (fix x) gets a :type-prescription rule that it is a nonnegative integer.

Now we prove a :linear-alias rule, actually a package of the six rules above.

(defthm nqthm-linear-arithmetic
       (implies (not (lessp x y))
  :rule-classes :linear-alias)
To mimic Nqthm behavior, we need a :rewrite rule to dispatch the cases generated by the case split caused by linearizing diff-expressions. This is not, strictly speaking, part of linear aliasing.
(defthm diff-0 (implies (lessp x y) (equal (diff x y) 0)))
Now we disable the nqthm-level functions. Henceforth, we do not expect to see ACL2-level functions in proofs about these concepts.
(in-theory (disable fix lessp plus diff))
Note that we can now prove such facts as
(thm (implies (and (lessp a b) (lessp b c)) (lessp a c)))
(thm (implies (lessp a (diff b c)) (lessp a b)))
and that the proofs appeal to linear arithmetic!

We can add new aliases (although they get new names). Note that we have to (a) define the function, (b) prove the :linear-alias rule -- probably enabling nqthm-level functions to do it, and (c) disable the new function so it survives rewriting and is around for linearization to de-alias.

(defun add1 (x) (+ (fix x) 1))                       ; (a)
(defthm add1-linear                                  ; (b) - see below
  (and (equal (add1 x) (+ (fix x) 1))
       (equal (fix (add1 x)) (add1 x)))
  :rule-classes :linear-alias
  :hints (("Goal" :in-theory (enable fix))))
(in-theory (disable add1))                           ; (c)
(thm (lessp x (add1 x)))
Observe that in (b) we prove two rules about add1: the one that relates it to + and the one that removes the fixes inserted by the first rule.

Next we illustrate the use of :linear lemmas in the context of linear aliasing. Consider the function:

(defun strpos (pat str)
; Find the position of pat in str.
  (cond ((atom str) 0)
        ((equal pat (car str)) 0)
        (t (add1 (strpos pat (cdr str))))))

(defun strlen (str) (cond ((atom str) 0) (t (add1 (strlen (cdr str))))))

We can prove and store the following :linear lemma about strpos.
(defthm strpos-length
  (not (lessp (strlen str) (strpos pat str)))
  :rule-classes :linear)
This event is interesting for two reasons. The proof illustrates linear aliasing. But the fact that this event is stored as a :linear lemma -- even though the conclusion is not about < -- also illustrates linear aliasing. In fact, this lemma is stored under (strpos pat str) with :fixer fix.

We can illustrate the storage and retrieval of the lemma directly by proving:

(thm (not (< (fix (strlen str)) (fix (strpos pat str)))))
But more commonly we will be interested in proofs that reach out for facts about strpos (implicitly fixing them for us) as in:
(thm (implies (lessp (strlen str) mx) 
              (lessp (strpos pat str) mx))).
This ends the extended example of linear aliasing lemmas.


make a :meta rule (a hand-written simplifier)
Major Section:  RULE-CLASSES

See rule-classes for a general discussion of rule classes and how they are used to build rules from formulas. Meta rules extend the ACL2 simplifier with hand-written code to transform certain terms to equivalent ones. To add a meta rule, the :corollary formula must establish that the hand-written ``metafunction'' preserves the meaning of the transformed term.

Example :corollary formulas from which :meta rules might be built are:

(equal (ev x a)                 ; Modify the rewriter to use fn to
       (ev (fn x) a))           ; transform terms.  The :trigger-fns
                                ; of the :meta rule-class specify
                                ; the top-most function symbols of
                                ; those x that are candidates for
                                ; this transformation.

(implies (and (pseudo-termp x) ; As above, but this illustrates (alistp a)) ; that without loss of generality we (equal (ev x a) ; may restrict x to be shaped like a (ev (fn x) a))) ; term and a to be an alist.

(implies (and (pseudo-termp x) ; As above (with or without the (alistp a) ; hypotheses on x and a) with the (ev (hyp-fn x) a)); additional restriction that the (equal (ev x a) ; meaning of (hyp-fn x) is true in (ev (fn x) a))) ; the current context. That is, the ; applicability of the transforma- ; tion may be dependent upon some ; computed hypotheses.

A non-nil list of function symbols must be supplied as the value of the :trigger-fns field in a :meta rule class object.

General Forms:
(implies (and (pseudo-termp x)    ; this hyp is optional
              (alistp a)          ; this hyp is optional
              (ev (hyp-fn x) a))  ; this hyp is optional
         (equiv (ev x a)
                (ev (fn x) a)))
where equiv is a known equivalence relation, x and a are distinct variable names, and ev is an evaluator function (see below), and fn is a function symbol, as is hyp-fn when provided. Both fn and hyp-fn should take a single argument. If fn and/or hyp-fn are guarded, their guards should be (implied by) pseudo-termp. See pseudo-termp. We say the theorem above is a ``metatheorem'' or ``metalemma'' and fn is a ``metafunction'', and hyp-fn is a ``hypothesis metafunction''.

We defer discussion of the case in which there is a hypothesis metafunction and for now address the case in which the other two hypotheses are present.

In the discussion below, we refer to the argument, x, of fn and hyp-fn as a ``term.'' When these metafunctions are executed by the simplifier, they will be applied to (the quotations of) terms. But during the proof of the metatheorem itself, x may not be the quotation of a term. If the pseudo-termp hypothesis is omitted, x may be any object. Even with the pseudo-termp hypothesis, x may merely ``look like a term'' but use non-function symbols or function symbols of incorrect arity. In any case, the metatheorem is stronger than necessary to allow us to apply the metafunctions to terms, as we do in the discussion below. We return later to the question of proving the metatheorem.

Suppose the general form of the metatheorem above is proved with the pseudo-termp and alistp hypotheses. Then when the simplifier encounters a term, (h t1 ... tn), that begins with a function symbol, h, listed in :trigger-fns, it applies the metafunction, fn, to the quotation of the term, i.e., it evaluates (fn '(h t1 ... tn)) to obtain some result, which can be written as 'val. If 'val is different from '(h t1 ... tn) and val is a term, then (h t1 ... tn) is replaced by val, which is then passed along for further rewriting. Because the metatheorem establishes the correctness of fn for all terms (even non-terms!), there is no restriction on which function symbols are listed in the :trigger-fns. Generally, of course, they should be the symbols that head up the terms simplified by the metafunction fn. See term-table for how one obtains some assistance towards guaranteeing that val is indeed a term.

The ``evaluator'' function, ev, is a function that can evaluate a certain class of expressions, namely, all of those composed of variables, constants, and applications of a fixed, finite set of function symbols, g1, ..., gk. Generally speaking, the set of function symbols handled by ev is chosen to be exactly the function symbols recognized and manipulated by the metafunctions being introduced. For example, if fn manipulates expressions in which 'equal and 'binary-append occur as function symbols, then ev is generally specified to handle equal and binary-append. The actual requirements on ev become clear when the metatheorem is proved. The standard way to introduce an evaluator is to use the ACL2 macro defevaluator, though this is not strictly necessary. See defevaluator for details.

Why are we justified in using metafunctions this way? Suppose (fn 'term1) is 'term2. What justifies replacing term1 by term2? The first step is to assert that term1 is (ev 'term1 a), where a is an alist that maps 'var to var, for each variable var in term1. This step is incorrect, because 'term1 may contain function symbols other than the ones, g1, ..., gk, that ev knows how to handle. But we can grow ev to a ``larger'' evaluator, ev*, an evaluator for all of the symbols that occur in term1 or term2. We can prove that ev* satisfies the constraints on ev. Hence, the metatheorem holds for ev* in place of ev, by functional instantiation. We can then carry out the proof of the equivalence of term1 and term2 as follows: Fix a to be an alist that maps the quotations of the variables of term1 and term2 to themselves. Then,

term1 = (ev* 'term1 a)      ; (1) by construction of ev* and a
      = (ev* (fn 'term1) a) ; (2) by the metatheorem for ev*
      = (ev* 'term2 a)      ; (3) by evaluation of fn
      = term2               ; (4) by construction of ev* and a
Note that in line (2) above, where we appeal to the (functional instantiation of the) metatheorem, we can relieve its (optional) pseudo-termp and alistp hypotheses by appealing to the facts that term1 is a term and a is an alist by construction.

Finally, we turn to the second case, in which there is a hypothesis metafunction. In that case, consider as before what happens when the simplifier encounters a term, (h t1 ... tn), where h is listed in :trigger-fns. This time, after it applies fn to '(h t1 ... tn) to obtain the quotation of some new term, 'val, it then applies the hypothesis metafunction, hyp-fn. That is, it evaluates (hyp-fn '(h t1 ... tn)) to obtain some result, which can be written as 'hyp-val. If hyp-val is not in fact a term, the metafunction is not used. Provided hyp-val is a term, the simplifier attempts to establish (by conventional backchaining) that this term is non-nil in the current context. If this attempt fails, then the meta rule is not applied. Otherwise, (h is replaced by val as in the previous case (where there was no hypothesis metafunction).

Why is it justified to make this extension to the case of hypothesis metafunctions? First, note that the rule

(implies (and (pseudo-termp x)
              (alistp a)
              (ev (hyp-fn x) a))
         (equal (ev x a)
                (ev (fn x) a)))
is logically equivalent to the rule
(implies (and (pseudo-termp x)
              (alistp a))
         (equal (ev x a)
                (ev (new-fn x) a)))
where (new-fn x) is defined to be (list 'if (hyp-fn x) (fn x) x). (If we're careful, we realize that this argument depends on making an extension of ev to an evaluator ev* that handles if and the functions manipulated by hyp-fn.) If we write 'term for the quotation of the present term, and if (hyp-fn 'term) and (fn 'term) are both terms, say hyp1 and term1, then by the previous argument we know it is sound to rewrite term to (if hyp1 term1 term). But since we have established in the current context that hyp1 is non-nil, we may simplify (if hyp1 term1 term) to term1, as desired.

We now discuss the role of the pseudo-termp hypothesis. (Pseudo-termp x) checks that x has the shape of a term. Roughly speaking, it insures that x is a symbol, a quoted constant, or a true list consisting of a lambda expression or symbol followed by some pseudo-terms. Among the properties of terms not checked by pseudo-termp are that variable symbols never begin with ampersand, lambda expressions are closed, and function symbols are applied to the correct number of arguments. See pseudo-termp.

There are two possible roles for pseudo-termp in the development of a metatheorem: it may be used as the guard of the metafunction and/or hypothesis metafunction and it may be used as a hypothesis of the metatheorem. Generally speaking, the pseudo-termp hypothesis is included in a metatheorem only if it makes it easier to prove. The choice is yours. (An extreme example of this is when the metatheorem is invalid without the hypothesis!) We therefore address ourselves the question: should a metafunction have a pseudo-termp guard? A pseudo-termp guard for a metafunction, in connection with other considerations described below, improves the efficiency with which the metafunction is used by the simplifier.

To make a metafunction maximally efficient you should (a) provide it with a pseudo-termp guard and exploit the guard when possible in coding the body of the function (see guards-and-evaluation, especially the section on efficiency issues), (b) verify the guards of the metafunction (see verify-guards), and (c) compile the metafunction (see comp). When these three steps have been taken the simplifier can evaluate (fn 'term1) by running the compiled ``primary code'' (see guards-and-evaluation) for fn directly in Common Lisp.

Before discussing efficiency issues further, let us review the for a moment the general case in which we wish to evaluate (fn `obj) for some :logic function. We must first ask whether the guards of fn have been verified. If not, we must evaluate fn by executing its logic definition. This effectively checks the guards of every subroutine and so can be slow. If, on the other hand, the guards of fn have been verified, then we can run the primary code for fn, provided 'obj satisfies the guard of fn. So we must next evaluate the guard of fn on 'obj. If the guard is met, then we run the primary code for fn, otherwise we run the logic code.

Now in the case of a metafunction for which the three steps above have been followed, we know the guard is (implied by) pseudo-termp and that it has been verified. Furthermore, we know without checking that the guard is met (because term1 is a term and hence 'term1 is a pseudo-termp). Hence, we can use the compiled primary code directly.

We strongly recommend that you compile your metafunctions, as well as all their subroutines. Guard verification is also recommended.