Create a flag-based ACL2::induction scheme for a mutual-recursion.
The
Generally speaking, writing a corresponding flag function is the first step toward proving any inductive property about mutually recursive definitions; more discussion below.
Example:
(make-flag flag-pseudo-termp ; flag function name (optional) pseudo-termp ; any member of the clique ;; optional arguments: :flag-mapping ((pseudo-termp term) (pseudo-term-listp list)) :defthm-macro-name defthm-pseudo-termp :flag-var flag :body :last ; use last body, not original :hints (("Goal" ...)) ; for the measure theorem ; (usually not necessary) :expand-with-original-defs t ; for the equivalence thm ; (usually not necessary) )
Here
The other arguments are optional:
To prove an inductive theorem about a mutually-recursive function, you usually have to effectively prove a single, big, ugly formula that has a different case about each function in the clique.
Normally, even with the flag function written for you, this would be a
tedious process. Here is an example of how you might prove by induction that
;; ACL2 can prove these are Booleans even without induction due to ;; type reasoning, so for illustration we'll turn these off so that ;; induction is required: (in-theory (disable (:type-prescription pseudo-termp) (:type-prescription pseudo-term-listp) (:executable-counterpart tau-system))) ;; Main part of the proof, ugly lemma with cases. Note that we ;; have to use :rule-classes nil here because this isn't a valid ;; rewrite rule. (local (defthm crux (cond ((equal flag 'term) (booleanp (pseudo-termp x))) ((equal flag 'list) (booleanp (pseudo-term-listp lst))) (t t)) :rule-classes nil :hints(("Goal" :induct (flag-pseudo-termp flag x lst))))) ;; Now we have to re-prove each part of the lemma so that we can use ;; it as a proper rule. (defthm type-of-pseudo-termp (booleanp (pseudo-termp x)) :rule-classes :type-prescription :hints(("Goal" :use ((:instance crux (flag 'term)))))) (defthm type-of-pseudo-term-listp (booleanp (pseudo-term-listp lst)) :rule-classes :type-prescription :hints(("Goal" :use ((:instance crux (flag 'list))))))
Obviously this is tedious and makes you say everything twice. Since the
steps are so standard,
(defthm-pseudo-termp (defthm type-of-pseudo-termp (booleanp (pseudo-termp x)) :rule-classes :type-prescription :flag term) (defthm type-of-pseudo-term-listp (booleanp (pseudo-term-listp lst)) :rule-classes :type-prescription :flag list))
It's worth understanding some of the details of what's going on here.
The macro automatically tries to induct using the induction scheme. But
(mutual-recursion (defun pseudo-termp (x) ...) (defun pseudo-term-listp (lst) ...))
So the theorem above only works without hints because we happened to choose
(defthm-pseudo-termp (defthm type-of-pseudo-termp (booleanp (pseudo-termp term)) :rule-classes :type-prescription :flag term) (defthm type-of-pseudo-term-listp (booleanp (pseudo-term-listp termlist)) :rule-classes :type-prescription :flag list) :hints(("Goal" :induct (flag-pseudo-termp flag term termlist))))
Localizing Theorems. Sometimes you may only want to export one of the theorems. For instance, if we only want to add a rule about the term case, but no the list case, we could do this:
(defthm-pseudo-termp (defthm type-of-pseudo-termp (booleanp (pseudo-termp x)) :rule-classes :type-prescription :flag term) (defthm type-of-pseudo-term-listp (booleanp (pseudo-term-listp lst)) :flag list :skip t))
Irrelevant Cases. Sometimes the theorem you want is inductive in such a way that some functions are irrelevant; nothing needs to be proved about them in order to prove the desired theorem about the others. The :skip keyword can be used with a theorem of T to do this:
(defthm-pseudo-termp (defthm type-of-pseudo-termp (booleanp (pseudo-termp x)) :rule-classes :type-prescription :flag term) (defthm type-of-pseudo-term-listp t :flag list :skip t))
Alternatively, you can provide the :skip-others keyword to the top-level macro and simply leave out the trivial parts:
(defthm-pseudo-termp (defthm type-of-pseudo-termp (booleanp (pseudo-termp x)) :rule-classes :type-prescription :flag term) :skip-others t)
Multiple Theorems. You may have more than one defthm form for a given
flag. For the main inductive proof, these are all simply conjoined
together (and their hints are simply appended together), but they are exported
as separate theorems and may have different
Legacy Syntax. There is an older, alternate syntax for
For advanced users, note that each individual "theorem" can have its own computed hints. For instance we can write:
(defthm-pseudo-termp (defthm type-of-pseudo-termp (booleanp (pseudo-termp term)) :rule-classes :type-prescription :flag term :hints ('(:expand ((pseudo-termp x))))) (defthm type-of-pseudo-term-listp (booleanp (pseudo-term-listp termlist)) :rule-classes :type-prescription :flag list :hints ('(:expand ((pseudo-term-listp lst))))) :hints(("Goal" :induct (flag-pseudo-termp flag term termlist))))
These hints are used during the mutually inductive proof. Under the top-level induction, we check the clause for the current subgoal to determine the hypothesized setting of the flag variable, and provide the computed hints for the appropriate case.
If you provide both a top-level hints form and hints on some or all of the
separate theorems, both sets of hints have an effect; try
You may use subgoal hints as well as computed hints, but they will not have any effect if the particular subgoal does not occur when those hints are in effect. We simply translate subgoal hints to computed hints:
("Subgoal *1/5.2" :in-theory (theory 'foo)) ---> (and (equal id (parse-clause-id "Subgoal *1/5.2")) '(:in-theory (theory 'foo)))
As mentioned above, if there is more than one defthm form for a given flag, the hints for all such forms are simply appended together; the hints given to one such form may affect what you might think of as the proof of another.