Isabelle Newbie
Isabelle Newbie

Reputation: 9378

How to fix loop in rewriting proof

I'm trying to model natural numbers in unary notation (O, (S O), (S (S O)), ...) in ACL2 and prove commutativity of addition. Here is my attempt:

; a NATURAL is 'O or a list ('S n') where n' is a NATURAL
(defun naturalp (n)
  (cond ((equal n 'O) t)
        (t (and (true-listp n)
                (equal (length n) 2)
                (equal (first n) 'S)
                (naturalp (second n))))))

(defun pred (n)
  (cond ((equal n 'O) 'O)
        ((naturalp n) (second n))))

(defun succ (n)
  (list 'S n))

(defun plus (n m)
  (cond ((equal n 'O) m)
        ((naturalp n) (succ (plus (pred n) m)))))

; FIXME: cannot prove this because rewriting loops...
(defthm plus_comm                                 
  (implies (and (naturalp n) (naturalp m))
           (iff (equal (plus n m) (plus m n)) t)))

This is probably not the most LISPy way of doing this, I'm used to languages with pattern matching.

My problem is as suggested by the comment: The prover loops, trying to prove more and more deeply nested versions of the same thing. How do I stop this? The manual does briefly mention looping rewrite rules, but it doesn't say anything about what to do about them.

My expectation was that this proof would fail, giving me hints as to what auxiliary lemmas are needed to complete it. Can I use the output from the looping proof to figure out a lemma that might stop the looping?

Upvotes: 0

Views: 93

Answers (1)

Jared Davis
Jared Davis

Reputation: 569

ACL2 can end up getting into different kinds of loops. One common kind is a rewriter loop, which usually is very evident. For example, the following:

(defun f (x) x)
(defun g (x) x)
(defthm f-is-g (implies (consp x) (equal (f x) (g x))))
(defthm g-is-f (implies (consp x) (equal (g x) (f x))))
(in-theory (disable f g))
(defthm loop (equal (f (cons a b)) (cons a b)))

Provokes a rewrite loop, and gives an informative debugging message:

HARD ACL2 ERROR in REWRITE:  The call depth limit of 1000 has been
exceeded in the ACL2 rewriter.  To see why the limit was exceeded,
first execute
  :brr t
and then try the proof again, and then execute the form (cw-gstack)
or, for less verbose output, instead try (cw-gstack :frames 30).  You
will then probably notice a loop caused by some set of enabled rules,
some of which you can then disable; see :DOC disable.  Also see :DOC
rewrite-stack-limit.

Unfortunately your example is getting into a different kind of loop. In particular, it looks like ACL2 is getting into a loop where

  • it inducts, but then gets to a subgoal it can't prove by rewriting, so
  • it inducts, but then gets to a subgoal it can't prove by rewriting, so
  • ...

It's not really as easy to see that this is what's going on. The thing I did was just run (set-gag-mode nil) before submitting the theorem, then inspect the output that was printed after interrupting the prover.

One way to avoid this is to give a hint, in particular you can tell ACL2 not to induct like this:

(defthm plus_comm
  (implies (and (naturalp n) (naturalp m))
           (iff (equal (plus n m) (plus m n)) t))
  :hints(("Goal" :do-not-induct t)))

But if you do that then it gets stuck right away, because probably you really do want to induct to prove this theorem. So what you really want to tell it is: "induct once, but don't induct more than that." The syntax is kind of goofy:

(defthm plus_comm
  (implies (and (naturalp n) (naturalp m))
           (iff (equal (plus n m) (plus m n)) t))
  :hints(("Goal"
          :induct t          ;; Induct once.
          :do-not-induct t   ;; But don't induct more than once.
      )))

This should leave you with a sensible checkpoint that you can then try to debug by adding rewrite rules or giving further hints.

Good luck!

Upvotes: 2

Related Questions