How to prove mathematical induction formulae in LEAN theorem prover? - theorem-proving

Can anyone help me understand how to write the proof of a simple result that can be easily obtained by induction, for example the formula for the sum of the first n natural numbers: 1+2+...+n = n(n+1)/2, using the LEAN theorem prover?

Here is my proof. You'll need mathlib for it to work.
import algebra.big_operators tactic.ring
open finset
example (n : ℕ) : 2 * (range (n + 1)).sum id = n * (n + 1) :=
begin
induction n with n ih,
{ refl },
{ rw [sum_range_succ, mul_add, ih, id.def, nat.succ_eq_add_one],
ring }
end
range (n + 1) is the set of natural numbers less than n + 1, i.e. 0 to n.
I used the finset.sum function. If s is a finset and f is a function, then
s.sum f is $\sum_{x \in s} f(x)$.
The induction tactic does induction. There are then two cases. The case n = 0 can be done with refl since this is nothing more than a computation.
The inductive step can be done with rw. Using a VSCode you can click after each comma in my proof to see what each rw does. This gets it into a form that the ring tactic will solve.

Related

How to do induction on BinNums.Z in Coq/

I'm trying to do a simple function that like this
Fixpoint find_0 (n : BinNums.Z) :=
match n with
Z0 => n
| Zpos p => find_0 p
| Zneg q => find_0 q
end.
But p is positive and not Z so this is ill typed.
If I try
Fixpoint find_0 (n : BinNums.Z) :=
match n with
Z0 => n
| Zpos p => find_0 (n - 1)
| Zneg q => find_0 (n + 1)
end.
then Coq can't verify that this is strong normalizing, the error
Recursive definition of find_0 is ill-formed.
In environment
find_0 : BinNums.Z -> BinNums.Z
n : BinNums.Z
p : positive
Recursive call to find_0 has principal argument equal to
"n - 1" instead of a subterm of "n".
Recursive definition is:
"fun n : BinNums.Z =>
match n with
| 0 => n
| Z.pos _ => find_0 (n - 1)
| BinInt.Z.neg _ => find_0 (n + 1)%Z
end".
What to do in this situation?
Regards
Since the definition of Bignums.Z:
Inductive Z : Set :=
Z0 : Z
| Zpos : positive -> Z
| Zneg : positive -> Z.
is not recursive, you cannot write recursive functions over it. Instead you write a simple non recursive Definition to handle the constructors of Bignums.Z and there you call recursive functions you define on positives. It is also good style to define the functions you need on positives separataely on positives.
Every recursive function in Coq must clearly have a decreasing aspect. For many inductive types, this decreasing aspect is provided naturally by the recursive structure of this inductive type. If you look at the definition of positive, you see this:
Inductive positive : Set :=
xI : positive -> positive | xO : positive -> positive | xH : positive.
When an object p of type positive fits the pattern xO q, q is visibly smaller than p, if only as a piece of data (and here because the agreed meaning of xO is multiplication by 2, q is also numerically smaller than p).
When looking at the data type for Z, you see that there is no recursion and thus no visible decreasing pattern, where the smaller object would also be of type Z, so you cannot write a recursive function using the Fixpoint approach.
However, there exists an extension of Coq, called Equations, that will make it possible to write the function you want. The trick is that you still need to explain that something is decreasing during the recursive call. This calls for an extra object, a relation that is known to have no infinite path. Such a relation is called well-founded. Here the relation we will use is called Zwf.
From Equations Require Import Equations.
Require Import ZArith Zwf Lia.
#[export]Instance Zwf_wf (base : Z) : WellFounded (Zwf base).
Proof.
constructor; apply Zwf_well_founded.
Qed.
Equations find0 (x : Z) : Z by wf (Z.abs x) (Zwf 0) :=
find0 Z0 := Z0; find0 (Zpos p) := find0 (Zpos p - 1);
find0 (Zneg p) := find0 (Zneg p + 1).
Next Obligation.
set (x := Z.pos p); change (Zwf 0 (Z.abs (x - 1)) (Z.abs x)).
unfold Zwf. lia.
Qed.
Next Obligation.
set (x := Z.neg p); change (Zwf 0 (Z.abs (x + 1)) (Z.abs x)).
unfold Zwf. lia.
Qed.
There is a little more work than for a direct recursive function using Fixpoint, because we need to explain that we are using a well founded relation, make sure the Equations extension will find the information (this is the purpose of the Instance part of the script. Then, we also need to show that each recursive call satisfies the decrease property. Here, what decreases is the numeric value of the absolute value of the integer.
The Equations tool will gives you a collection of theorems to help reason on the find0 function. In particular, theorems find0_equation1, find0_equation2, and find0_equation3 really express that we defined a function that follows the algorithm you intended.

Coq theorem proving: Simple fraction law in peano arithmetic

I am learning coq and am trying to prove equalities in peano arithmetic.
I got stuck on a simple fraction law.
We know that (n + m) / 2 = n / 2 + m / 2 from primary school.
In peano arithmetic this does only hold if n and m are even (because then division produces correct results).
Compute (3 / 2) + (5 / 2). (*3*)
Compute (3 + 5) / 2. (*4*)
So we define:
Theorem fraction_addition: forall n m: nat ,
even n -> even m -> Nat.div2 n + Nat.div2 m = Nat.div2 (n + m).
From my understanding this is a correct and provable theorem.
I tried an inductive proof, e.g.
intros n m en em.
induction n.
- reflexivity.
- ???
Which gets me into the situation that
en = even (S n)
and IHn : even n -> Nat.div2 n + Nat.div2 m = Nat.div2 (n + m), so i don't find a way to apply the induction hypothesis.
After long research of the standard library and documentation, i don't find an answer.
You need to strengthen your induction hypothesis in cases like this.
One way of doing this is by proving an induction principle like this one:
From Coq Require Import Arith Even.
Lemma nat_ind2 (P : nat -> Prop) :
P 0 ->
P 1 ->
(forall n, P n -> P (S n) -> P (S (S n))) ->
forall n, P n.
Proof.
now intros P0 P1 IH n; enough (H : P n /\ P (S n)); [|induction n]; intuition.
Qed.
nat_ind2 can be used as follows:
Theorem fraction_addition n m :
even n -> even m ->
Nat.div2 n + Nat.div2 m = Nat.div2 (n + m).
Proof.
induction n using nat_ind2.
(* here goes the rest of the proof *)
Qed.
You can also prove your theorem without induction if you are ok with using the standard library.
If you use Even m in your hypothesis (which says exists n, m = 2*m) then you can use simple algebraic rewrites with lemmas from the standard library.
Require Import PeanoNat.
Import Nat.
Goal forall n m, Even n -> Even m -> n / 2 + m / 2 = (n+m)/2.
inversion 1; inversion 1.
subst.
rewrite <- mul_add_distr_l.
rewrite ?(mul_comm 2).
rewrite ?div_mul; auto.
Qed.
The question mark just means "rewrite as many (zero or more) times as possible".
inversion 1 does inversion on the first inductive hypothesis in the goal, in this case first Even n and then Even m. It gives us n = 2 * x and m = 2 * x0 in the context, which we then substitute.
Also note even_spec: forall n : nat, even n = true <-> Even n, so you can use even if you prefer that, just rewrite with even_spec first...

Why does nesting the induction tactic also nest the inductive hypotheses under a lambda?

Theorem mult_comm : forall m n : nat,
m * n = n * m.
Proof.
intros.
induction n.
- simpl. rewrite (mult_0_r m). reflexivity.
- simpl.
rewrite <- IHn.
induction m.
simpl. reflexivity.
simpl.
The above is from the Software Foundation's second chapter.
1 subgoal
m, n : nat
IHn : S m * n = n * S m
IHm : m * n = n * m -> m * S n = m + m * n
______________________________________(1/1)
S (n + m * S n) = S (m + (n + m * n))
I am really confused as to what IHm is supposed to be here. The way I understand it, Coq tactics get compiled under the hood to some functional program, but I am really not sure what is going on here. I am pretty sure that this is not I intended it to do.
What I wanted to do is something like the following Idris program.
add_comm : {a,b : Nat} -> a + b = b + a
add_assoc : {a,b,c : Nat} -> (a + b) + c = a + (b + c)
total
mult_comm : {m,n : Nat} -> (m * n) = n * m
mult_comm {m = Z} {n = Z} = Refl
mult_comm {m = Z} {n = (S k)} = mult_comm {m=Z} {n=k}
mult_comm {m = (S k)} {n = Z} = mult_comm {m=k} {n=Z}
mult_comm {m = (S k)} {n = (S j)} =
let prf1 = mult_comm {m=k} {n=S j}
prf2 = mult_comm {m=S k} {n=j}
prf3 = mult_comm {m=k} {n=j}
prf_add_comm = add_comm {a=k} {b=j}
prf_add_assoc = add_assoc {a=k} {b=j} {c=j*k}
prf_add_assoc' = add_assoc {a=j} {b=k} {c=j*k}
in
rewrite prf1 in
rewrite sym prf2 in
rewrite prf3 in
rewrite sym prf_add_assoc in
rewrite sym prf_add_assoc' in
rewrite (add_comm {a=k} {b=j}) in
Refl
More specifically, I need prf1, prf2 and prf3 which I get using recursive calls to mult_comm. In Coq the two of the proofs are stuck in a lambda and I am not sure how that happened. I see that Coq's induction tactic is not doing what I think it should be doing.
In addition to the explanation of the above, let me also ask is there more introductory material to Coq than Software Foundations just in case I get stuck like this again on some tactic? Note that I know how to solve this in Coq as I've found the solution online.
I've tried tackling the SF book unsuccessfully back in 2016 as an introduction to dependently typed programming and now with the benefit of hindsight, I see that Little Typer and the Idris book are much better in that regard.
When you call the induction tactic, Coq uses heuristics to determine the predicate P : nat -> Prop that you want to prove by induction. Before calling induction for the second time, the proof state looks like this:
m, n : nat
IHn : m * n = n * m
============================
m * S n = m + m * n
What happened is that Coq decided to include the premise IHn in the induction predicate, which was inferred to be
P m := m * n = n * m -> m * S n = m + m * n
which is exactly what you had in your induction hypothesis. In this case, you could argue that it was silly for Coq to use the premise, but there are cases where dropping it would result in an unprovable goal. For instance, consider the following proof attempt:
Lemma double_inj : forall n m, n + n = m + m -> n = m.
Proof.
intros n m H.
induction n as [|n IH].
(* ... *)
If H were dropped after calling induction, you would have to prove forall n m, n = m, which clearly does not hold.
This example is one of the reasons why it is often a bad idea to call induction multiple times in a single Coq proof. As we suggest in that exercise in Software Foundations, it is better to prove an auxiliary lemma, since you can be explicit about the induction predicate. For this example, there are other options as well. You could, for instance, call clear IHn to drop the IHn premise, which would lead Coq to the correct predicate. The ssreflect proof language, which now ships with Coq, has a different tactic for performing induction called elim, which allows you to be more explicit in the choice of the predicate.
I agree with your final comment, but I should add that it is not the goal of Software Foundations to be an introduction to dependently typed programming. Though Coq supports this paradigm, it is generally cumbersome to write such programs directly, and much easier to use tactics to prove lemmas about simply typed programs. For instance, your proof of mult_comm is accepted by Idris because its termination checker is smart enough to recognize all recursive calls as decreasing, even though they are not decreasing with respect to a fixed argument (in the second clause, n decreases, whereas in the third m does). This is not possible in Coq, and you must split the definition into multiple recursive functions, one for each argument, or use well-founded induction on pairs of natural numbers, which would be overkill for this example.
Adam Chlipala has another Coq textbook called CPDT that you might want to check out. However, I don't think you will find a comprehensive description of Coq's tactics there, either. Like induction, many tactics rely on heuristics and are hard to explain in detail.
On a final note, Matthieu Sozeau has developed a package called Equations that makes dependently typed programming in Coq much closer to Idris or Agda. If you find this style of proving more intuitive, you could try to use it.

Writing proofs of simple arithmetic in Coq

I would like to prove simple things like for every natural number n, there exists a natural number k such that:
(2*n + 1)^2 = 8*k + 1
How does one go about such a proof? I thought of dividing it into cases when n is odd or even, but I do not know how to do so in Coq. Also, is there an inbuilt power (exponent) operator in Coq?
Yes, there are many built-in functions, you just need the right set of imports or open the right notation scope.
One easy way to do the proof is to use induction and some automation like ring, omega or lia tactics.
From Coq Require Import Arith Psatz.
Goal forall n, exists k, (2*n + 1)^2 = 8*k + 1.
Proof.
induction n as [| n [m IH]].
- now exists 0.
- exists (S n + m). rewrite Nat.pow_2_r in *. lia.
Qed.
Here is an alternative proof, using the same idea as found in the proof by #Yves:
From Coq Require Import Arith Psatz.
Fact exampleNat n : exists k, (2 * n + 1) ^2 = 8 * k + 1.
Proof.
exists (n * (S n) / 2).
assert (H : Nat.even (n * (S n)) = true) by
now rewrite Nat.even_mul, Nat.even_succ, Nat.orb_even_odd.
apply Nat.even_spec in H as [m H]; rewrite (Nat.mul_comm 2) in H.
rewrite H, Nat.div_mul, Nat.pow_2_r; lia.
Qed.
Observe that this proof schema works for the integer numbers too provided you change everything from namespace Nat to Z (S to Z.succ, etc.).
We can follow the initial plan suggested in the question of reasoning by cases on whether the input is odd or even, only representing parity by the value of n mod 2 and using a boolean equality test to express the alternative.
The proof can also be made with relative integers instead of natural numbers.
From Coq Require Import ZArith Psatz.
Open Scope Z_scope.
Lemma example n : exists k, (2 * n + 1) ^2 = 8 * k + 1.
Proof.
assert (vn : n = 2 * (n / 2) + n mod 2) by now apply Z_div_mod_eq.
destruct (n mod 2 =? 0) eqn: q.
- rewrite Z.eqb_eq in q; rewrite vn, q.
exists ((2 * (n / 2) + 1) * (n / 2)); ring.
- enough (vm : n mod 2 = 1)
by now rewrite vn, vm; exists (2 * (n / 2) ^ 2 + 3 * (n / 2) + 1); ring.
rewrite Z.eqb_neq in q.
assert (0 <= n mod 2 < 2) by now apply Z_mod_lt.
lia.
Qed.
This proof is not as nice and elementary as the one by #Anton on Jan 24th, though.

Compute with a recursive function defined by well-defined induction

When I use Function to define a non-structurally recursive function in Coq, the resulting object behaves strangely when a specific computation is asked. Indeed, instead of giving directly the result, the Eval compute in ... directive return a rather long (typically 170 000 lines) expression. It seems that Coq cannot evaluate everything, and therefore returns a simplified (but long) expression instead of just a value.
The problem seems to come from the way I prove the obligations generated by Function. First, I thought the problem came from the opaque terms I used, and I converted all the lemmas to transparent constants. By the way, is there a way to list the opaque terms appearing in a term ? Or any other way to turn opaque lemmas into transparent ones ?
I then remarked that the problem came more precisely from the proof that the order used is well-founded. But I got strange results.
For example, I define log2 on the natural numbers by repeatedly applying div2. Here is the definition:
Function log2 n {wf lt n} :=
match n with
| 0 => 0
| 1 => 0
| n => S (log2 (Nat.div2 n))
end.
I get two proof obligations. The first one checks that n respects the relation lt in the recursive calls and can be proved easily.
forall n n0 n1 : nat, n0 = S n1 -> n = S (S n1) -> Nat.div2 (S (S n1)) < S (S n1)
intros. apply Nat.lt_div2. apply le_n_S. apply le_0_n.
The second one checks that lt is a well-founded order. This is already proved in the standard library. The corresponding lemma is Coq.Arith.Wf_nat.lt_wf.
If I use this proof, the resulting function behaves normally. Eval compute in log24 10. returns 3.
But if I want to do the proof myself, I do not always get this behaviour. First, if I end the proof with Qed instead of Defined, the result of the computation (even on small numbers) is a complex expression and not a single number. So I use Defined and try to use only transparent lemmas.
Lemma lt_wf2 : well_founded lt.
Proof.
unfold well_founded. intros n.
apply (lemma1 n). clear n.
intros. constructor. apply H.
Defined.
Here, lemma1 is a proof of the well-founded induction on the natural numbers. Here again, I can use already existing lemmas, such as lt_wf_ind, lt_wf_rec, lt_wf_rec1 located in Coq.Arith.Wf_nat, or even well_founded_ind lt_wf. The first one does not work, it seems this is because it is opaque. The three others work.
I tried to prove it directly using the standard induction on the natural numbers, nat_ind. This gives:
Lemma lemma1 : forall n (P:nat -> Prop),
(forall n, (forall p, p < n -> P p) -> P n) -> P n.
Proof.
intros n P H. pose proof (nat_ind (fun n => forall p, p < n -> P p)).
simpl in H0. apply H0 with (n:=S n).
- intros. inversion H1.
- intros. inversion H2.
+ apply H. exact H1.
+ apply H1. assumption.
- apply le_n.
Defined.
With this proof (and some variants of it), log2 has the same strange behaviour. And this proof seems to use only transparent objects, so maybe the problem is not there.
How can I define a Function that returns understandable results on specific values ?
I've managed to pin-point the place that causes troubles: it's inversion H2. in lemma1. It turns out we don't need that case-analysis and intuition can finish the proof (it doesn't pattern-match on H2):
Lemma lemma1 : forall n (P:nat -> Prop),
(forall n, (forall p, p < n -> P p) -> P n) -> P n.
Proof.
intros n P H. pose proof (nat_ind (fun n => forall p, p < n -> P p)).
simpl in H0. apply H0 with (n:=S n).
- intros. inversion H1.
- intros. intuition.
- apply le_n.
Defined.
If we use lemma1 with this proof, the computation of log2 10 results in 3.
By the way, here is my version of lt_wf2 (it lets us compute as well):
Lemma lt_wf2 : well_founded lt.
Proof.
unfold well_founded; intros n.
induction n; constructor; intros k Hk.
- inversion Hk.
- constructor; intros m Hm.
apply IHn; omega.
(* OR: apply IHn, Nat.lt_le_trans with (m := k); auto with arith. *)
Defined.
I believe the
Using Coq's evaluation mechanisms in anger blog post by Xavier Leroy explains this kind of behavior.
it eliminates the proof of equality between the heads before recursing over the tails and finally deciding whether to produce a left or a right. This makes the left/right data part of the final result dependent on a proof term, which in general does not reduce!
In our case we eliminate the proof of inequality (inversion H2.) in the proof of lemma1 and the Function mechanism makes our computations depend on a proof term. Hence, the evaluator can't proceed when n > 1.
And the reason inversion H1. in the body of the lemma doesn't influence computations is that for n = 0 and n = 1, log2 n is defined within the match expression as base cases.
To illustrate this point, let me show an example when we can prevent evaluation of log2 n on any values n and n + 1 of our choice, where n > 1 and nowhere else!
Lemma lt_wf2' : well_founded lt.
Proof.
unfold well_founded; intros n.
induction n; constructor; intros k Hk.
- inversion Hk. (* n = 0 *)
- destruct n. intuition. (* n = 1 *)
destruct n. intuition. (* n = 2 *)
destruct n. intuition. (* n = 3 *)
destruct n. inversion Hk; intuition. (* n = 4 and n = 5 - won't evaluate *)
(* n > 5 *)
constructor; intros m Hm; apply IHn; omega.
Defined.
If you use this modified lemma in the definition of log2 you'll see that it computes everywhere except n = 4 and n = 5. Well, almost everywhere -- computations with large nats can result in stack overflow or segmentation fault, as Coq warns us:
Warning: Stack overflow or segmentation fault happens when working with
large numbers in nat (observed threshold may vary from 5000 to 70000
depending on your system limits and on the command executed).
And to make log2 work for n = 4 and n = 5 even for the above "flawed" proof, we could amend log2 like this
Function log2 n {wf lt n} :=
match n with
| 0 => 0
| 1 => 0
| 4 => 2
| 5 => 2
| n => S (log2 (Nat.div2 n))
end.
adding the necessary proofs at the end.
The "well-founded" proof must be transparent and can't rely on pattern-matching on proof objects because the Function mechanism actually uses the lt_wf lemma to compute the decreasing termination guard. If we look at the term produced by Eval (in a case where evaluation fails to produce a nat), we'll see something along these lines:
fix Ffix (x : nat) (x0 : Acc (fun x0 x1 : nat => S x0 <= x1) x) {struct x0}
It's easy to see that x0 : Prop, so it gets erased when extracting the functional program log2 into, say OCaml, but Coq's internal evaluation mechanism have to use it to ensure termination.
The reduction behavior of functions defined by well-founded recursion in Coq is generally not very good, even when you declare your proofs to be transparent. The reason for this is that arguments of well-foundedness usually need to be done with complicated proof terms. Since these proofs terms end up appearing in well-founded recursive definitions, "simplifying" your function will make all of those proof terms appear, as you noticed.
It is easier to rely on custom tactics and lemmas to reduce functions defined this way. First, I would recommend favoring Program Fixpoint over Function, because the latter is much older and (I think) less well maintained. Thus, you would end up with a definition like this:
Require Import Coq.Numbers.Natural.Peano.NPeano.
Require Import Coq.Program.Wf.
Require Import Coq.Program.Tactics.
Program Fixpoint log2 n {wf lt n} :=
match n with
| 0 => 0
| 1 => 0
| n => S (log2 (Nat.div2 n))
end.
Next Obligation.
admit.
Qed.
Now, you just need to use the program_simpl tactic to simplify calls to log2. Here's an example:
Lemma foo : log2 4 = 2.
Proof.
program_simpl.
Qed.