Simple numeric theorem not accepted by ACL2 - theorem-proving

ACL2 doesn't prove the following theorem:
(defthm thm-0
(implies
(and
(integerp n)
(oddp n)
(>= n 1))
(oddp (* n n))))
My guess is that an induction scheme that steps by two over the odd numbers should be applied:
(defun odd-induction (x)
"Induct by going two steps at a time"
(if (or (zp x) (equal x 1))
x
(+ 2 (odd-induction (1- x)))))
(defthm thm-0
(implies
(and
(integerp n)
(oddp n)
(>= n 1))
(oddp (* n n)))
:hints (("Goal" :induct (odd-induction n))) :rule-classes nil)
The theorem is still not accepted. An explanation of where I'm mistaken, or too optimistic, would be very much appreciated.
Addition:
As this similar theorem is accepted without an induction hint, I suspect something else is wrong with thm-0.
(defthm thm-1
(implies
(and
(integerp o)
(integerp e)
(oddp o)
(evenp e))
(evenp (* o e))))

First, sorry for the long response time. I think the ACL2 community often responds more quickly (i.e. within a day or two) to queries on the ACL2 Help mailing list than on places like Stack Overflow, though some of us do attempt to check the ACL2 tag here from time to time.
ACL2 defines evenness and oddness by saying that x is even if (integerp (/ x 2)) and x is odd if x is not even. Unfortunately these definitions are a little unwieldy and not very symmetric to each other, so it's very possible for some theorems about evenp or oddpto require a lot of coaxing before ACL2 can prove them, even if it's able to prove other theorems about evenp or oddp very easily.
For example, ACL2 is able to prove your thm-1 by the following reasoning:
e is even, meaning that (* e 1/2) is an integer. We want to prove that (* o e) is even, i.e. that (* (* o e) 1/2) is an integer. We expand out this latter expression to (* o e 1/2) using associativity of *. But since o is an integer and (* e 1/2) is known to be an integer, then (* o e 1/2) (which is really (* o (* e 1/2))) must also be an integer.
Here's ACL2's failed attempt at trying to prove thm-0:
n is odd, meaning that (* n 1/2) is not an integer. We want to prove that (* (* n n) 1/2) is not an integer. Hmm. We expand out this latter expression to (* n n 1/2). Hmm. We normalize the (* n 1/2) inside this latter expression to (* 1/2 n) because we know * is commutative and our heuristics say that the constant 1/2 is "lighter" than the variable n. Hmm. Can't think of anything else to simplify, so let's move on to the next step in the waterfall, which is induction. But we can't figure out what to induct on because no recursive functions are involved.
This results in the goal of (not (integerp (* n 1/2 n))) and the message "no induction schemes are suggested".
What made the first proof easier than the second? The first proof relied on the property that the set of integers is closed under multiplication, i.e. an integer times an integer must be an integer. In this proof, the analogous step would be to prove that (* n n 1/2) is a non-integer by using the fact that n is an integer and (* n 1/2) is a non-integer.
Unfortunately, it is not true that an integer times a non-integer must be an integer -- for example, (* 2 1/2) is an integer. So why can't (* n (* n 1/2)) be an integer? To explain why, we need to be able to express the fact (* n 1/2) is not just any non-integer -- in particular, it's a rational number whose denominator is even, and because n is odd, multiplying by n cannot cancel the 2 from the denominator of (* n 1/2). From a human reasoning perspective,that's the most direct way I can think of to fix ACL2's unsuccessful attempt at a proof above, but formulating that argument in a way ACL2 can understand might be somewhat difficult -- I'd take a different approach altogether and use induction, as you have attempted to do.
So unfortunately while your thm-0 and thm-1 seem similar to a human, they look pretty different to ACL2.
Now, how can we prove the theorem you wanted? Here's my solution. (Keep in mind that there are often many different ways to solve a certain problem in ACL2, and other more experienced users might come up with a totally different solution from mine.)
First of all, your odd-induction function is unfortunately not doing what you intended. The function recurses on itself with an argument that's decremented by 1, adds 2 to the result, and returns. But when you're creating a function just so that you can use its induction scheme, it doesn't matter what the function returns, just the particular way in which it recurses -- i.e., what all the recursive calls are, what their arguments are, and what conditions must hold for each recursive call to occur.
In this case, there's one recursive call, with argument (1- x), which occurs when neither (zp x) nor (equal x 1) is true. Everything else about the function is irrelevant. Thus the induction scheme has a base case of (or (zp x) (equal x 1)) and an inductive step (P (1- x)) => (P x). But what you wanted was for the inductive step to be (P (- x 2)) => (P x). So here is a better induction scheme function:
(defun odd-induction (x)
"Induct by going two steps at a time"
(or (zp x)
(equal x 1)
(odd-induction (- x 2))))
Let's try the final theorem, just to see what the induction looks like. Note that your induction scheme actually only works for natural numbers, not all integers, so I'm going to leave in the restriction that we're only proving this property about positive odd numbers, not negative ones.
(defthm thm-0
(implies (and (natp n)
(oddp n))
(oddp (* n n)))
:hints (("Goal" :induct (odd-induction n))))
This fails, but let's ignore the checkpoints at the end and look at the top of the output, where the induction scheme was chosen:
We have been told to use induction. One induction scheme is suggested
by the induction hint.
We will induct according to a scheme suggested by (ODD-INDUCTION N).
This suggestion was produced using the :induction rule ODD-INDUCTION.
If we let (:P N) denote *1 above then the induction scheme we'll use
is
(AND (IMPLIES (AND (NOT (ZP N))
(NOT (EQUAL N 1))
(:P (+ -2 N)))
(:P N))
(IMPLIES (AND (NOT (ZP N)) (EQUAL N 1))
(:P N))
(IMPLIES (ZP N) (:P N))).
So you can see that in the inductive step it's going to try to prove (:P N) using (:P (+ -2 N)) as a hypothesis. (Here, :P refers to the goal which you're trying to prove by induction.)
To avoid the mess of directly reasoning about dividing by two and whether or not things are integers, let's disable both evenp and oddp. It'd also be simpler to just reason about evenp and its negation, and forget about oddp, so we'll prove a theorem that facilitates this before we disable evenp and oddp.
(defthm oddp-is-not-evenp
(equal (oddp x)
(not (evenp x))))
(in-theory (disable evenp oddp))
Let's try submitting thm-0 again and pay attention to the checkpoints this time.
Subgoal *1/3.2
(IMPLIES (AND (NOT (ZP N))
(NOT (EQUAL N 1))
(NOT (EVENP (+ 4 (* -2 N) (* -2 N) (* N N))))
(<= 0 N)
(NOT (EVENP N)))
(NOT (EVENP (* N N))))
Subgoal *1/3.1
(IMPLIES (AND (NOT (ZP N))
(NOT (EQUAL N 1))
(EVENP (+ -2 N))
(<= 0 N)
(NOT (EVENP N)))
(NOT (EVENP (* N N))))
From this we can see that ACL2 has begun induction to prove *1, and has gotten stuck the third case (*1/3), which it has split into subgoals, two of which (*1/3.1 and *1/3.2) it was unable to prove.
Glancing at the hypotheses of *1/3.1, we see that they claim that nis not even, but (+ -2 n) is even. We can also see that n is an integer because (not (zp n)) holds. This is impossible since -2is even and n is even, so their sum should be even. If we prove that fact, Subgoal *1/3.1 should go away.
(defthm even-plus-odd-is-odd
(implies (and (evenp x)
(not (evenp y)))
(not (evenp (+ x y))))
:hints (("Goal" :in-theory (enable evenp))))
Note that ACL2 would already "know" this fact if we hadn't disabled evenp earlier, making the definition of evenp unavailable. In fact, that's how we were able to prove this theorem, by temporarily enabling evenp again during the proof. Often it is worthwhile to make ACL2 "forget" a bunch of stuff and then selectively re-prove only particular facts you actually need.
Now let's try submitting thm-0 once again. This time we only get one subgoal:
Subgoal *1/3.2
(IMPLIES (AND (NOT (ZP N))
(NOT (EQUAL N 1))
(NOT (EVENP (+ 4 (* -2 N) (* -2 N) (* N N))))
(<= 0 N)
(NOT (EVENP N)))
(NOT (EVENP (* N N))))
OK, how can we make this one go away? IMO, the best way to look at this subgoal is to flip one of the hypotheses into the conclusion. The ACL2 rewriter functions on "clauses", which are a disjunction of "literals", each literal being an arbitrary term. When a goal looks like
(implies (and [A]
[B]
[C])
[D])
for some terms A, B, C, and D, what ACL2 is actually doing internally is trying to prove
(or (not [A])
(not [B])
(not [C])
D)
ACL2 will try to simplify each of the four disjuncts in the above expression in turn, and when it is simplifying one, the other three are being viewed as hypotheses. So going back to our subgoal, it may be easier to see how to solve the problem if we rearrange the clause to imagine what ACL2 will see when it is simplifying the literal containing the algebraic expression (+ 4 (* -2 n) (* -2 n) (* n n)):
(IMPLIES (AND (NOT (ZP N))
(NOT (EQUAL N 1))
(EVENP (* N N))
(<= 0 N)
(NOT (EVENP N)))
(EVENP (+ 4 (* -2 N) (* -2 N) (* N N))))
Aside: You might notice a contradiction in the hypotheses -- we have one hypothesis saying that n is not even, and another saying that (* n n) is even! -- but the fact that that's a contradiction is what we're trying to prove in the first place, so we can't resolve this subgoal by noticing that contradiction.
Now, how can we prove that (+ 4 (* -2 n) (* -2 n) (* n n)) is even? Well, it's the sum of four even numbers. 4 is obviously an even number -- ACL2 could just directly evaluate the expression (evenp 4) to satisfy itself of this fact. (* -2 n) is even because n is even and an even number (-2) times any integer is an even number. And (* n n) is even because one of the hypotheses says so.
So, if we can teach ACL2 the facts about evenness used in the above paragraph, we should be good to go. First we prove that the sum of even numbers is even.
(defthm even-plus-even-is-even
(implies (and (evenp x)
(evenp y))
(evenp (+ x y)))
:hints (("Goal" :in-theory (enable evenp))))
That one worked just fine. Note that proving it for the two-summand case is sufficient, because the rewrite rule produced will telescope out to larger numbers of summands. That is, to prove that (+ a b c d) is even when a, b, c, and d are even, ACL2 will in turn prove that (+ c d), (+ b c d), and finally (+ a b c d) are even, using the even-plus-even-is-even rewrite rule three times.
Next we try to prove that an even number times any integer is even.
(defthm even-times-integer-is-even
(implies (and (evenp x)
(integerp y))
(evenp (* x y)))
:hints (("Goal" :in-theory (enable evenp))))
This one didn't work. The checkpoint ACL2 prints says it got stuck trying to prove that (* x 1/2 y) is an integer given that (* 1/2 x) is an integer. It would be nice if we could rearrange (* x 1/2 y) to (* y 1/2 x) (which is the same as (* y (* 1/2 x))) so that we could use the closure of the integers under multiplication.
One way to do it is to just prove the theorem with y and x swapped in the conclusion, then produce the rewrite we actually wanted by specifying a :corollary:
(defthm even-times-integer-is-even
(implies (and (evenp x)
(integerp y))
(evenp (* y x))) ; note that x and y are swapped
:hints (("Goal" :in-theory (enable evenp)))
:rule-classes
((:rewrite :corollary
(implies (and (evenp x)
(integerp y))
(evenp (* x y)))))) ; x and y in the correct order
The tweak of changing (* y x) to (* x y) between the main theorem statement and the corollary is simple enough for ACL2 to be satisfied with storing the rewrite rule in the (* x y) form even though the theorem was proved using the (* y x) form.
I can think of some other ways to prove the theorem even-times-integer-is-even which are perhaps a little less pat than the one above, but I won't go into them as they'd require yet more lemmas.
Now that even-plus-even-is-even and even-times-integer-is-even are successfully proved, Subgoal *1/3.2 should go away -- and indeed it does.
For reference, here's the whole body of code that was used to prove thm-0:
(defun odd-induction (x)
"Induct by going two steps at a time"
(or (zp x)
(equal x 1)
(odd-induction (- x 2))))
(defthm oddp-is-not-evenp
(equal (oddp x)
(not (evenp x))))
(in-theory (disable evenp oddp))
(defthm even-plus-odd-is-odd
(implies (and (evenp x)
(not (evenp y)))
(not (evenp (+ x y))))
:hints (("Goal" :in-theory (enable evenp))))
(defthm even-plus-even-is-even
(implies (and (evenp x)
(evenp y))
(evenp (+ x y)))
:hints (("Goal" :in-theory (enable evenp))))
(defthm even-times-integer-is-even
(implies (and (evenp x)
(integerp y))
(evenp (* y x))) ; note that x and y are swapped
:hints (("Goal" :in-theory (enable evenp)))
:rule-classes
((:rewrite :corollary
(implies (and (evenp x)
(integerp y))
(evenp (* x y)))))) ; x and y in the correct order
(defthm thm-0
(implies (and (natp n)
(oddp n))
(oddp (* n n)))
:hints (("Goal" :induct (odd-induction n))))
This was my sort of maximally lazy way of trying to prove the specific theorem you wanted, but of course there are more robust ways to approach it, e.g. to build a whole collection of useful theorems about evenp and oddp, from which specific cases like thm-0 would follow. You can find one such example in the community book coi/super-ihs/evenp.

Related

Can anyone help me with a recursive function in racket?

I am writing a recursive function. But the question requires you not to use the exponential function. Can anyone show me how to get larger powers by multiplying smaller powers by a?
Input a=2 n=4. Then get[2, 4, 8, 16]
Input a=3 n=4. Then get[3 9 27 81].
I was trying to multiply a by a each time, so when I input 2 and 4. I get [2 4 16 256]. So what should I do?
Here is what I have written:
(define (input a n)
(if (= n 0)
'()
(append (cdr (list [* a a] a))
(let ((a (* a a)))
(input a (- n 1))))))
You are approaching the problem wrong, you really need two recursive functions (one to build the list and one to build each element). I am assuming you are allowed to use local, but if you aren't you could move that into a helper function.
(define (build-sqr-list a n)
(local [(define (sqr-recurse a n)
(if (= n 0)
1
(* a (sqr-recurse a (sub1 n)))))]
(if (= n 0)
'()
(cons (sqr-recurse a n) (build-sqr-list a (sub1 n))))))

Trying to get this code to work, can't understand where to put the argument in and keep getting errors

Define the function iota1(n, m) that takes positive integers n, m with n < m as input, and outputs the list (n,n+1,n+2,...,m)
I've tried switching the code around multiple times but cannot seem to get it to function and display a list the right way
(define (iota1 n m)
(if (eq? n 0)
'()
(append (iota1 (< n m) (+ n 1)) (list n))))
There's a few oddities to the code you provided, which I've formatted for readability:
(define (iota1 n m)
(if (eq? n 0)
'()
(append (iota (< n m) (+ n 1))
(list n))))
The first is that the expression (< n m) evaluates to a boolean value, depending on whether n is less than m or not. When you apply iota to (< n m) in the expression (iota (< n m) (+ n 1)), you are giving iota a boolean value for its first argument instead of a positive integer.
Secondly, the use of append is strange here. When constructing a list in Racket, it's much more common to use the function cons, which takes as arguments a value, and a list, and returns a new list with the value added to the front. For example,
(append '(3) (append '(4) (append '(5) '()))) ==> '(3 4 5)
(cons 3 (cons 4 (cons 5 '()))) ==> '(3 4 5)
It's a good idea to opt for using cons instead of append because it's simpler, and because it is faster, since cons does not traverse the entire list like append does.
Since this sounds a bit like a homework problem, I'll leave you with a "code template" to help you find the answer:
; n : integer
; m : integer
; return value : list of integers
(define (iota1 n m)
(if (> n m) ; base case; no need to do work when n is greater than m
... ; value that goes at the end of the list
(cons ... ; the value we want to add to the front of the list
(iota1 ... ...)))) ; the call to iota, generating the rest of the list
Welcome to the racket world, my version is here:
#lang racket
(define (iota1 n m)
(let loop ([loop_n n]
[result_list '()])
(if (<= loop_n m)
(loop
(add1 loop_n)
(cons loop_n result_list))
(reverse result_list))))

SICP - Multiplication through addition

I am using the book SICP and attempting to solve this exercise:
1.2.4 Exponentiation
Exercise 1.18. Using the results of exercises 1.16 and 1.17, devise
a procedure that generates an iterative process for multiplying two
integers in terms of adding, doubling, and halving and uses a
logarithmic number of steps
I am trying to solve this with the following code:
(define (double x)
(+ x x))
(define (halve x)
(floor (/ x 2)))
(define (* a b)
(define (iter count accumulate)
(cond ((= count 1) accumulate)
((even? a) (iter (halve count) (+ accumulate (double b))))
(else empty)))
(iter a 0))
As you might see, I am trying to deal with even numbers first.
I am using the SICP wiki as my solutions-guide. They suggest some tests to see if the code works:
(* 2 4)
(* 4 0)
What I do not get is that my code passes on these two first tests, dealing only with even numbers.
However, when I try some big numbers which are multiples of two, the code fails. I checked the result using Python. For instance,
(IN PYTHON)
2**100
>> 1267650600228229401496703205376
2**98
>> 316912650057057350374175801344
a = 2**100
b = 2**98
a*b
>> 401734511064747568885490523085290650630550748445698208825344
When I use my function inside Dr. Racket with these values I get a different result:
(* 1267650600228229401496703205376 316912650057057350374175801344)
My result is: 63382530011411470074835160268800, which is wrong, as Python built-in functions suggest.
Why this is happening?
The recursive step seems wrong, and what's that empty doing there? also, what happens if b is negative? this solution should work:
(define (mul a b)
(define (iter a b acc)
(cond ((zero? b) acc)
((even? b) (iter (double a) (halve b) acc))
(else (iter a (- b 1) (+ a acc)))))
(if (< b 0)
(- (iter a (- b) 0))
(iter a b 0)))
For example:
(mul 1267650600228229401496703205376 316912650057057350374175801344)
=> 401734511064747568885490523085290650630550748445698208825344

"unfold" for common lisp?

I learned quite a bit of scheme from SICP but am more interested in common lisp now. I know common lisp's fold is reduce, with special arguments for left or right folding, but what is the equivalent of unfold? Googling has not helped much. In fact I get the impression there is no unfold???
Common Lisp has (loop ... collect ...). Compare
(loop for x from 1 to 10 collect (* x x))
with its equivalence using unfold:
(unfold (lambda (x) (> x 10))
(lambda (x) (* x x))
(lambda (x) (+ x 1))
1)
In general, (unfold p f g seed) is basically
(loop for x = seed then (g x) until (p x) collect (f x))
Edit: fix typo
The common lisp hyperspec doesn't define an unfold function, but you can certainly write your own. Its scheme definition translates almost symbol for symbol.

How would I express this Scheme function more clearly?

(define (repeated f n)
if (= n 0)
f
((compose repeated f) (lambda (x) (- n 1))))
I wrote this function, but how would I express this more clearly, using simple recursion with repeated?
I'm sorry, I forgot to define my compose function.
(define (compose f g) (lambda (x) (f (g x))))
And the function takes as inputs a procedure that computes f and a positive integer n and returns the procedure that computes the nth repeated application of f.
I'm assuming that (repeated f 3) should return a function g(x)=f(f(f(x))). If that's not what you want, please clarify. Anyways, that definition of repeated can be written as follows:
(define (repeated f n)
(lambda (x)
(if (= n 0)
x
((repeated f (- n 1)) (f x)))))
(define (square x)
(* x x))
(define y (repeated square 3))
(y 2) ; returns 256, which is (square (square (square 2)))
(define (repeated f n)
(lambda (x)
(let recur ((x x) (n n))
(if (= n 0)
args
(recur (f x) (sub1 n))))))
Write the function the way you normally would, except that the arguments are passed in two stages. It might be even clearer to define repeated this way:
(define repeated (lambda (f n) (lambda (x)
(define (recur x n)
(if (= n 0)
x
(recur (f x) (sub1 n))))
(recur x n))))
You don't have to use a 'let-loop' this way, and the lambdas make it obvious that you expect your arguments in two stages.
(Note:recur is not built in to Scheme as it is in Clojure, I just like the name)
> (define foonly (repeat sub1 10))
> (foonly 11)
1
> (foonly 9)
-1
The cool functional feature you want here is currying, not composition. Here's the Haskell with implicit currying:
repeated _ 0 x = x
repeated f n x = repeated f (pred n) (f x)
I hope this isn't a homework problem.
What is your function trying to do, just out of curiosity? Is it to run f, n times? If so, you can do this.
(define (repeated f n)
(for-each (lambda (i) (f)) (iota n)))