This is the resource page for Section 2 of Logic and Computation, CSU 290 (Spring 2008). Welcome.

The purpose of this page is to simply complete the lectures, partly to keep us in sync with the other section, partly because there is only so much time and much interesting things to discuss.

These notes are meant to supplement the lectures, the readings and the lecture notes (available on the course web site) and not replace them.

Feel free to comment at the bottom of the page if you have any questions or anything is unclear. (Select "Add Comment" under "Add Content" down below.) I will generally merge your comments and questions into the main text when I get to them.

**What you should do right now**: Make sure you are logged into the wiki. (Select "Log In" under "Your Account" below if not, use you CCIS credentials to log in.) Look at the bottom of the page, under "Other Features", top right icon is an envelope. Press that button. That will register you as *watching this page*. Basically, every time the page changes, you get an email saying so. Keeps you from having to check the page regularly.

## February 15: Formal proof for `~(t=nil)`

Here is a complete formal proof for `~(t=nil)`

using the axioms I gave in class. (This is the proof that I started on Monday, but ran out of time 5 steps into the proof.)

You should be able to understand all the steps in that proof, if not be able to come up with the proof yourself. We will not ask you to come up with these kind of formal proofs in this course. Instead, we will use the more informal "chain of equals" or "chain of relations" approach I presented in class.

In the proof below, I write `~A`

for "not `A`

" and `A & B`

for "`A`

and `B`

".

1. ~(equal (car (cons x y) x) x) = nil [ axiom for equal/car/cons 2. ~(equal (car (cons a b) a) a) = nil) [ instantiation of 1 with x->a, y->b

Okay, so 2 is something we will use later on. Let's remember it.

3. ~(x = y) => (equal x y) = nil [ axiom for equal 4. ~(car (cons a b)) = a => (equal (car (cons a b)) a) = nil [ instantiation of 3 with x->(car (cons a b)), y->a 5. ~(~(car (cons a b)) = a) [ by MT applied to 4 and 2 6. ~(~A) => A [ tautology of Boolean logic 7. ~(~(car (cons a b)) = a) => (car (cons a b)) = a [ instantiation of 6 with A->(car (cons a b)) 8. (car (cons a b)) = a [ by MP applied to 7 and 5 9. x=y => (equal x y) = y [ axiom for equal 10. (car (cons a b)) = a => (equal (car (cons a b)) a) = t [ instantiation of 9 with x->(car (cons a b)),y->a 11. (equal (car (cons a b)) a) = t [ by MP applied to 10 and 8

Excellent. So now we can see that 2 and 11 together essentially give us that `t /= nil`

.

We just have to work some to get that to come out of the proof.

12. (A & B => C) => (A & ~C => ~B) [ tautology of Boolean logic (check it!) 13. ((equal (car (cons a b)) a) = t & t = nil => (equal (car (cons a b)) a) = nil)) [ instantiation of 12 with => ((equal (car (cons a b)) a) = t [ A->(equal (car (cons a b)) a) = t & ~(equal (car (cons a b)) a) = nil)) [ B->(t=nil) => ~(t = nil)) [ C->(equal (car (cons a b)) a) = nil 14. x = y & y = z => x = z [ axiom of transitivity 15. (equal (car (cons a b)) a) = t [ instantiation of 14 with & t = nil => [ x->(equal (car (cons a b)) a) (equal (car (cons a b)) a) = nil [ y->t and z->nil 16. (equal (car (cons a b)) a) = t [ by MP applied to 13 and 15 & ~(equal (car (cons a b)) a) = nil => ~(t = nil)

And we are just about done. We have both antecedents of the implication in 16.

17. ~(t=nil) [ by MP applied to 16, 11, and 2

## February 20: Review of Proving Theorems

Here is what we have seen until now as far as proving theorems are concerned.

We reviewed Boolean logic and the concept of a tautology of Boolean logic. Recall that a tautology is just a Boolean formula that is always true, irrespectively of the truth values you give to the Boolean variables in the formula.

We shall take Boolean (aka propositional) reasoning for granted.

We introduced the ACL2 logic, as essentially Boolean logic augmented with the ability to reason about basic formulas of the form EXP1 = EXP2, where EXP1 and EXP2 are ACL2 expressions.

The ACL2 logic is defined formally using the following axioms and rules of inferences:

- Every tautology of Boolean logic is an axiom
`x = x`

(reflexivity)`x = y => y = x`

(symmetry)`x = y /\ y = z => x = z`

(transitivity)`x1 = y1 /\ ... /\ xn = yn => (f x1 ... xn) = (f y1 ... yn)`

for every function symbol`f`

of arity n- From
`F`

derive`F`

/s (where`F`

is an arbitrary formula, and s is a substitution of ACL2 expressions for free variables in`F`

) - From
`F`

and`F => G`

derive`G`

(modus ponens)

We also have axioms corresponding to the different primitives of the language. The basic ones are:

`x = y => (equal x y) = t`

`x /= y => (equal x y) = nil`

(where`/=`

represents "not equal to")`x = nil => (if x y z) = z`

`x /= nil => (if x y z) = x`

`(car (cons x y)) = x`

`(cdr (cons x y)) = y`

`consp (cons x y) = t`

`consp (nil) = nil`

`(integerp x) = t => (consp x)=nil`

Additionally, we can take as axioms the basic properties of arithmetic that you have seen in high school and discrete maths. Thus, when proving theorems, you will be free to perform standard simplification of arithmetic.

A formal proof of `F`

is a sequence of formulas such that every formula in the sequence is either an axiom or derived from previous formulas using an inference rule, and such that the last formula of the sequence is `F`

itself.

I gave you a formal proof of `t /= nil`

above. We shall not write a lot of formal proof, and you certainly will not have to write any of them.

Instead, we shall use some proof techniques to prove formulas that have a certain form. These techniques do not work for all formulas, but they work for enough of them to be interesting.

**If the formula you want to prove is of the form EXP1 = EXP2**, then one technique is to use a sequence of equalities to equate EXP1 and EXP2:

EXP1 = .... = EXP2

where all the equalities are justified using the axioms or the inference rules above. By transitivity of =, this clearly shows that EXP1 = EXP2.

Alternatively, it is sometimes easier to simplify both EXP1 and EXP2 into a common expression EXP3.

EXP1 = ... = EXP3 EXP2 = ... = EXP3

Again, it should be pretty clear that this proves that EXP1 = EXP2. (By transitivity of = once again.)

**If the formula you want to prove is of the form HYPS => EXP1 = EXP2**, where HYPS is some hypotheses (formulas), then you can use a variant of the above, that is, prove that EXP1 = EXP2 using a sequence of equalities (or simplify EXP1 and EXP2 to the same expression EXP3), except that when you derive the equalities you can in addition to the axioms and inference rules use the hypotheses in HYPS to help you.

## February 25: Solutions to Exam 3 Proof Questions

Recall the definitions:

(defun add99 (l) (if (endp l) nil (cons (cons 99 (car l)) (add99 (cdr l))))) (defun same-lenp (l n) (if (endp l) t (if (equal (len (car l)) n) (same-lenp (cdr l) n) nil))) (defun len (l) (if (endp l) 0 (+ 1 (len (cdr l)))))

Question 2. Prove `(consp x) => (len (car (add99 x))) = (+ (len (car x)) 1)`

(len (car (add99 x))) = {def of add99 } (len (car (if (endp x) nil (cons (cons 99 (car x)) (add99 (cdr x)))))) = { (endp x) = nil } (len (car (cons (cons 99 (car x)) (add99 (cdr x))))) = {car-cons axiom} (len (cons 99 (car x))) = {def of len } (if (endp (cons 99 (car x))) 0 (+ 1 (len (cdr (cons 99 (car x)))))) = {endp(cons ...)=nil} (+ 1 (len (cdr (cons 99 (car x))))) = {cdr-cons axiom} (+ 1 (len (car x)))

Question 3. Prove `(endp x) => (same-lenp (add99 x) y)`

(same-lenp (add99 x) (+ y 1)) = {def of add99} (same-lenp (if (endp x) nil ...)) = { (endp x) = t } (same-lenp nil (+ y 1)) = {def of same-lenp } (if (endp nil) t ...) = { (endp nil)=t } t

Question 4. Prove `((same-lenp x y) /\ (consp x) /\ (same-lenp (add99 (cdr x)) (+ y 1))) => (same-lenp (add99 x) (+ y 1))`

(You can use the fact that `((same-lenp x y) /\ (consp x)) => (+ 1 (len (car x))) = (+ 1 y)`

.)

(same-lenp (add99 x) (+ y 1)) = {def of add99} (same-lenp (if (endp x) nil (cons (cons 99 (car x)) (add99 (cdr x)))) (+ y 1)) = {(endp x) = nil} (same-lenp (cons (cons 99 (car x)) (add99 (cdr x))) (+ y 1)) = {def of same-lenp} (if (endp (cons (cons 99 (car x)) (add99 (cdr x)))) t (if (= (len (car (cons (cons 99 (car x)) (add99 (cdr x))))) (+ y 1)) (same-lenp (cdr (cons (cons 99 (car x)) (add99 (cdr x)))) (+ y 1)) nil)) = { (endp (cons ...)) = nil } (if (= (len (car (cons (cons 99 (car x)) (add99 (cdr x))))) (+ y 1)) (same-lenp (cdr (cons (cons 99 (car x)) (add99 (cdr x)))) (+ y 1)) nil) = { car-cons and cdr-cons axioms} (if (= (len (cons 99 (car x))) (+ y 1)) (same-lenp (add99 (cdr x)) (+ y 1)) nil) = { def of len } (if (= (if (endp (cons 99 (car x))) 0 (+ 1 (len (cdr (cons 99 (car x)))))) (+ y 1)) (same-lenp (add99 (cdr x)) (+ y 1))) nil) = { (endp (cons ...)) = nil (if (= (+ 1 (len (cdr (cons 99 (car x)))) (+ y 1))) (same-lenp (add99 (cdr x)) (+ y 1))) nil) = { car-cons axiom } (if (= (+ 1 (len (car x)) (+ 1 y))) (same-lenp (add99 (cdr x)) (+ y 1))) nil) = { by fact above } (same-lenp (add99 (cdr x)) (+ y 1)) = { by hypothesis } t

## February 26: On Termination

Here is the argument that, at least in Scheme, it is impossible to write a function that correctly determines whether another function terminates. We argue by contradiction.

Suppose that we could write a function (terminates? f x) that returns #t (true) when (f x) terminates, and returns #f (false) when (f x) does not terminate. I don't care how terminates? is written - just suppose you managed to write it. I will not show that you get something completely absurd as a result.

In particular, I can now write the following perfectly legal scheme function:

(define (loop-forever) (loop-forever)) (define (oops f x) (if (terminates? oops empty) (loop-forever) 1))

My question: does (oops empty) terminate?

Let's see. It either does or doesn't. So let's consider both cases.

- If (oops empty) terminates, then by assumption (terminates? oops empty) must be true, and therefore, by the code of oops, (oops empty) must loop forever, i.e., not terminate, contradicting that (oops empty) terminates.

- if (oops empty) does not terminate, then by assumption (terminates? oops empty) must be false, and therefore by the code of oops, (oops empty) returns 1 immediately, i.e., terminates, contradicting that (oops empty) does not terminate.

Because we get a contradiction no matter what, it must be that what we initially assumed was wrong, i.e., that terminates? works correctly. In other words, we cannot write a correct terminates? function in Scheme.

It turns out that this argument can be made to work for any programming language, but you'll have to wait for CSU 390 to see it.

One approach to arguing that a recursive function terminates is to argue that we are making progress towards the base case. The design recipe that we gave you and that you learned in 211 is in fact meant to ensure exactly this, at least for the kind of functions that we have seen until now.

It is sometimes a little subtle to argue that you are indeed making progress towards termination. In particular, consider the following ACL2 function:

(defun foo (n) (cond ((zp n) 0) ((= n 1) 1) ((evenp n) (foo (/ n 2))) ((oddp n) (foo (+ n 1)))))

(Assume that we have correct definitions for `evenp`

and `oddp`

.) I claim that this terminates for all inputs. Clearly, if the input is not a natural number, it terminates immediately. (Why?) Otherwise, can we argue that we are making progress towards the base cases (either 0 or 1)? Here are two arguments: the first, the one raised in class on Monday, is that that even though when the input is odd the recursive call is done on a bigger number (that therefore does not immediately progresses towards the base case), (foo (+ n 1)) will be called on an even number, which means that at the following iteration, foo will be invoked on (/ (+ n 1) 2), which is smaller than n. Thus, even though at every step we are not making progress towards the base case, we are making progress towards the base case at either every step or every two steps, depending on whether the input is even or odd. That's actually sufficient to establish termination. (Why?)

Here's another way of thinking about it that actually shows progress towards termination at every step. Write the input as a binary number, and consider the following number <n> derived from the input n:

<n> = the number of 1's in the binary representation of n + 2 * (the length of n in binary) - the number of 0's in the binary representation of n to the right of the rightmost 1.

Thus,

- 4 is 100 in binary, so <4> = 1 + (2 * 3) - 2 = 1 + 6 - 2 = 5,
- 5 is 101 in binary, so <5> = 2 + (2 * 3) - 0 = 2 + 6 = 8,
- 6 is 110 in binary, so <6> = 2 + (2 * 3) - 1 = 2 + 6 - 1 = 7.

You can check that at every recursive step of the function, foo is called on an input n whose <n> is strictly smaller than the original input to the function. And we are making progress towards the base case, with <1> = 3.

However, this doesn't work for every function, even those that look similar to the above. One of the most famous examples is the following so-called Collatz function:

(defun collatz (n) (cond ((zp n) 0) ((= n 1) 1) ((evenp n) (collatz (/ n 2))) ((oddp n) (collatz (+ (* 3 n) 1)))))

It is unknown whether this function terminates on all inputs. Plot out a few calls for small values of n, and you'll get a sense for how long some of the iterations take. Termination has been checked for all natural numbers up to 10^16, but for all we know it fails to terminate on some input greater than 10^17. We just don't know enough about number theory to say anything else.

I posted some more mathematical references to this function here.