Major Section: RULE-CLASSES
As described elsewhere (see rule-classes), ACL2 rules are treated as
implications for which there are zero or more hypotheses hj
to prove. In
particular, rules of class :
rewrite
may look like this:
(implies (and h1 ... hn) (fn lhs rhs))Variables of
hi
are said to occur free in the above :rewrite
rule if they do not occur in lhs
or in any hj
with j<i
. (To be
precise, here we are only discussing those variables that are not in the
scope of a let
/let*
/lambda
that binds them.) We also refer
to these as the free variables of the rule. ACL2 issues a warning or
error when there are free variables in a rule, as described below.
(Variables of rhs
may be considered free if they do not occur in lhs
or in any hj
. But we do not consider those in this discussion.)
In general, the free variables of rules are those variables occurring in
their hypotheses (not let
/let*
/lambda
-bound) that are not not
bound when the rule is applied. For rules of class :
linear
and
:
forward-chaining
, variables are bound by a trigger term.
(See rule-classes for a discussion of the :trigger-terms
field).
Let us discuss the method for relieving hypotheses of rewrite rules with
free variables. Similar considerations apply to linear and
forward-chaining rules, while for other rules (in particular,
type-prescription rules), only one binding is tried, much as described
in the discussion about :once
below.
See free-variables-examples for examples of how this all works, including illustration of how the user can exercise some control over it.
thm
below succeed?
(defstub p2 (x y) t)Consider what happens when the proof of the(defaxiom p2-trans (implies (and (p2 x y) (p2 y z)) (equal (p2 x z) t)) :rule-classes ((:rewrite :match-free :all)))
(thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d)))
thm
is attempted. The ACL2
rewriter attempts to apply rule p2-trans
to the conclusion, (p2 a d)
.
So, it binds variables x
and z
from the left-hand side of the
conclusion of p2-trans
to terms a
and d
, respectively, and then
attempts to relieve the hypotheses of p2-trans
. The first hypothesis of
p2-trans
, (p2 x y)
, is considered first. Variable y
is free in
that hypothesis, i.e., it has not yet been bound. Since x
is bound to
a
, the rewriter looks through the context for a binding of y
such
that (p2 a y)
is true, and it so happens that it first finds the term
(p2 a b)
, thus binding y
to b
. Now it goes on to the next
hypothesis, (p2 y z)
. At this point y
and z
have already been
bound to b
and d
; but (p2 b d)
cannot be proved.
So, in order for the proof of the thm
to succeed, the rewriter needs to
backtrack and look for another way to instantiate the first hypothesis of
p2-trans
. Because :match-free :all
has been specified, backtracking
does take place. This time y
is bound to c
, and the subsequent
instantiated hypothesis becomes (p2 c d)
, which is true. The application
of rule (p2-trans)
succeeds and the theorem is proved.
If instead :match-free :all
had been replaced by :match-free :once
in
rule p2-trans
, then backtracking would not occur, and the proof of the
thm
would fail.
Next we describe in detail the steps used by the rewriter in dealing with free variables.
The ACL2 rewriter uses the following sequence of steps to relieve hypotheses
with free variables. (1) If the hypothesis has the form (equal var term)
where var
is free and no variable of term
is free, then bind var
to the result of rewriting term
in the current context. (2) Look for a
binding of the free variables of the hypothesis so that the corresponding
instance of the hypothesis is known to be true in the current context. (3)
Search all enable
d, hypothesis-free rewrite rules of the form
(equiv lhs rhs)
, where lhs
has no variables (other than those bound
by let
, let*
, or lambda
), rhs
is known to be true in the
current context, and equiv
is typically equal
but can be any
equivalence relation appropriate for the current context (see congruence);
then attempt to bind the free variables so that the instantiated hypothesis
is lhs
. If all attempts fail and the hypothesis is a call of force
or case-split
, where forcing is enabled (see force) then the
hypothesis is relieved, but in the split-off goals, all free variables are
bound to unusual names that call attention to this odd situation.
When a rewrite or linear rule has free variables in the hypotheses,
the user generally needs to specify whether to consider only the first
instance found in steps (2) and (3) above, or instead to consider them all.
Below we discuss how to specify these two options as ``:once
'' or
``:all
'' (the default), respectively.
Is it better to specify :once
or :all
? We believe that :all
is
generally the better choice because of its greater power, provided the user
does not introduce a large number of rules with free variables, which has
been known to slow down the prover due to combinatorial explosion in the
search (Steps (2) and (3) above).
Either way, it is good practice to put the ``more substantial'' hypotheses
first, so that the most likely bindings of free variables will be found first
(in the case of :all
) or found at all (in the case of :once
). For
example, a rewrite rule like
(implies (and (p1 x y) (p2 x y)) (equal (bar x) (bar-prime x)))may never succeed if
p1
is nonrecursive and enabled, since we may well
not find calls of p1
in the current context. If however p2
is
disabled or recursive, then the above rule may apply if the two hypotheses
are switched. For in that case, we can hope for a match of (p2 x y)
in
the current context that therefore binds x
and y
; then the rewriter's
full power may be brought to bear to prove (p1 x y)
for that x
and
y
.Moreover, the ordering of hypotheses can affect the efficiency of the rewriter. For example, the rule
(implies (and (rationalp y) (foo x y)) (equal (bar x) (bar-prime x)))may well be sub-optimal. Presumably the intention is to rewrite
(bar x)
to (bar-prime x)
in a context where (foo x y)
is explicitly known to
be true for some rational number y
. But y
will be bound first to the
first term found in the current context that is known to represent a rational
number. If the 100th such y
that is found is the first one for which
(foo x y)
is known to be true, then wasted work will have been done on
behalf of the first 99 such terms y
-- unless :once
has been
specified, in which case the rule will simply fail after the first binding of
y
for which (rationalp y)
is known to be true. Thus, a better form
of the above rule is almost certainly the following.
(implies (and (foo x y) (rationalp y)) (equal (bar x) (bar-prime x)))
Specifying `once' or `all'. One method for specifying :once
or
:all
for free-variable matching is to provide the :match-free
field of
the :rule-classes
of the rule, for example, (:rewrite :match-free :all)
.
See rule-classes. However, there are global events that can be used
to specify :once
or :all
; see set-match-free-default and
see add-match-free-override. Here are some examples.
(set-match-free-default :once) ; future rules without a :match-free field ; are stored as :match-free :once (but this ; behavior is local to a book) (add-match-free-override :once t) ; existing rules are treated as ; :match-free :once regardless of their ; original :match-free fields (add-match-free-override :once (:rewrite foo) (:rewrite bar . 2)) ; the two indicated rules are treated as ; :match-free :once regardless of their ; original :match-free fields
Some history. Before Version 2.7 the ACL2 rewriter performed Step (2)
above first. More significantly, it always acted as though :once
had
been specified. That is, if Step (2) did not apply, then the rewriter took
the first binding it found using either Steps (1) or (3), in that order, and
proceeded to relieve the remaining hypotheses without trying any other
bindings of the free variables of that hypothesis.
Interaction with the break-rewrite utility. You may find that the break-rewrite utility (see break-rewrite) gives surprising error messages such as the following when a hypothesis has free variables.
:HYP 1 contains a free variable that we could not bind appropriately. See :DOC free-variables.What may be surprising is that the hypothesis being ``blamed'' (in this case, 1) is not the one that you imagine is failing. Consider the following example.
(defstub p (x) t)(defaxiom p-closed (implies (and (p y) (< x y)) (p x)) :rule-classes ((:rewrite :match-free :all)))
:brr t
:monitor (:rewrite p-closed) t
Below is an ACL2 transcript illustrating this issue. In that proof attempt,
ACL2 does successfully bind the free variable y
of rule p-closed
to
a
. However, it is then unable to relieve the second hypothesis of
p-closed
, namely (< x y)
, where x
is bound to a
and y
is
bound to (* a a)
. The rewriter then backtracks to look for new potential
bindings of y
besides a
, and is unable to find any. So it blames the
failure on the first hypothesis rather than on the second. (Even if
:match-free :once
had been specified, the result would be the same. More
information may be provided in future releases, especially if users express a
need for improvement.)
ACL2 !>(thm (implies (and (integerp a) (< 1 a) (p (* a a))) (p a)))Incidentally, if one proves the rule....
(1 Breaking (:REWRITE P-CLOSED) on (P A): 1 ACL2 >:eval 1x (:REWRITE P-CLOSED) failed because :HYP 1 contains a free variable that we could not bind appropriately. See :DOC free-variables. 1 ACL2 >
(defthm helper (implies (and (integerp a) (< 1 a)) (< a (* a a))))then the above theorem can be proved by ACL2.