I would like to understand the use of "elim" and "induction" on Coq...
Why ?
Because i have been tried to do some exercises and didn't not understand why i must use sometimes the "elim" and other times "induction"...
For example :
Lemma parte2_1_b : forall l, sum(rev l) = sum l.
Proof.
intro.
induction l.
simpl.
reflexivity.
simpl.
SearchRewrite(_++_).
SearchAbout(_++_).
rewrite parte2_1_a.
simpl.
rewrite IHl.
SearchAbout(_+_).
rewrite <- plus_n_O.
(*omega.
Qed*)
(*
ring.
Qed.
*)
SearchRewrite(_+_).
rewrite plus_comm.
reflexivity.
Qed.
The other example :
Lemma parte2_1_c : forall l1 l2, Prefix l1 l2 -> sum l1 <= sum l2.
Proof.
intros.
elim H.
intros.
simpl.
SearchPattern(_<=_).
apply le_0_n.
intros.
simpl.
SearchPattern(_<=_).
(*omega*)
apply plus_le_compat_l.
assumption.
Qed.
I've been looking for the documentation on the website, and i still don't understand on how am i supposed to choose the one correctly...
May someone helps me, please ?
Missing Functions :
Fixpoint sum (l: list nat) : nat := match l with
| nil => 0
| a::t => a + sum t
end.
The other function :
Lemma parte2_1_a : forall l1 l2, sum (l1++l2) = sum l1 + sum l2.
Proof.
intros.
induction l1.
simpl.
reflexivity.
simpl.
(*
omega.
Qed.
*)
rewrite IHl1.
SearchRewrite(_+_).
rewrite plus_assoc.
reflexivity.
Qed.
I think that now, you will be able to run the program now.
As #Ptival said, elim and induction perform almost the exact same action on the goal. The main difference you will see is that the shape of the goal might be a bit different: for example elim leaves the induction hypothesis in the goal:
Lemma parte2_1_b : forall l, sum(rev l) = sum l.
Proof.
intro.
induction l.
simpl.
reflexivity.
1 subgoal
l : list nat
====================================================================== (1/1)
forall (a : nat) (l0 : list nat),
sum (rev l0) = sum l0 -> sum (rev (a :: l0)) = sum (a :: l0)
whereas induction names it in the context:
Lemma parte2_1_b : forall l, sum(rev l) = sum l.
Proof.
intro.
induction l.
simpl.
reflexivity.
1 subgoal
a : nat
l : list nat
IHl : sum (rev l) = sum l
====================================================================== (1/1)
sum (rev (a :: l)) = sum (a :: l)
In the first case, if you do clear l; intros a l IHl, you will end up having the exact same goal as in the second one.
There exists very particular uses for elim that induction can't do, but the only relevant cases I know are for hardcore crazy users, which is not really useful most of the time. I have used Coq for years now and have I encountered such a case only once, and I didn't really need it in the end, so I advise you don't bother yourself and stick to using induction for now.
As a general question-related advice, it's hard to help you when we can't run the code you give. Giving a working minimal example or a pointer to the definitions you use is appreciated.
As for your question, elim x. and induction x. seem to be doing very similar things. As far as I can see, the difference seems to be that induction performs a bit more work by:
introducing the induction hypothesis in your context, whereas elim leaves them quantified over in the goal
cleaning up the context of the variable being inducted upon, whereas elim leaves it there
They might have some more specific differences in behaviors, but as far as proving is concerned, I'm fairly sure they are similarly powerful (in that they both call the inductor of your type). So you shouldn't be too concerned I believe. I personally never use elim and always use induction, because I tend to like the extra work it does.
Related
The following example is from chapter Poly of the Software Foundations book.
Definition fold_length {X : Type} (l : list X) : nat :=
fold (fun _ n => S n) l 0.
Theorem fold_length_correct : forall X (l : list X),
fold_length l = length l.
Proof.
intros.
induction l.
- simpl. reflexivity.
- simpl.
1 subgoal
X : Type
x : X
l : list X
IHl : fold_length l = length l
______________________________________(1/1)
fold_length (x :: l) = S (length l)
I expected it to simplify a step here on the left side. It certainly should be able to.
Theorem fold_length_correct : forall X (l : list X),
fold_length l = length l.
Proof.
intros.
induction l.
- simpl. reflexivity.
- simpl. rewrite <- IHl. simpl.
1 subgoal
X : Type
x : X
l : list X
IHl : fold_length l = length l
______________________________________(1/1)
fold_length (x :: l) = S (fold_length l)
During the running of the tests I had an issue where simpl would refuse to dive in, but reflexivity did the trick, so I tried the same thing here and the proof succeeded.
Note that one would not expect reflexivity to pass given the state of the goal, but it does. In this example it worked, but it did force me to do the rewrite in the opposite direction of what I intended originally.
Is it possible to have more control over simpl so that it does the desired reductions?
For the purposes of this answer, I'll assume the definition of fold is something along the lines of
Fixpoint fold {A B: Type} (f: A -> B -> B) (u: list A) (b: B): B :=
match u with
| [] => b
| x :: v => f x (fold f v b)
end.
(basically fold_right from the standard library). If your definition is substantially different, the tactics I recommend might not work.
The issue here is the behavior of simpl with constants that have to be unfolded before they can be simplified. From the documentation:
Notice that only transparent constants whose name can be reused in the recursive calls are possibly unfolded by simpl. For instance a constant defined by plus' := plus is possibly unfolded and reused in the recursive calls, but a constant such as succ := plus (S O) is never unfolded.
This is a bit hard to understand, so let's use an example.
Definition add_5 (n: nat) := n + 5.
Goal forall n: nat, add_5 (S n) = S (add_5 n).
Proof.
intro n.
simpl.
unfold add_5; simpl.
exact eq_refl.
Qed.
You'll see that the first call to simpl didn't do anything, even though add_5 (S n) could be simplified to S (n + 5). However, if I unfold add_5 first, it works perfectly. I think the issue is that plus_5 is not directly a Fixpoint. While plus_5 (S n) is equivalent to S (plus_5 n), that isn't actually the definition of it. So Coq doesn't recognize that its "name can be reused in the recursive calls". Nat.add (that is, "+") is defined directly as a recursive Fixpoint, so simpl does simplify it.
The behavior of simpl can be changed a little bit (see the documentation again). As Anton mentions in the comments, you can use the Arguments vernacular command to change when simpl tries to simplify. Arguments fold_length _ _ /. tells Coq that fold_length should be unfolded if at least two arguments are provided (the slash separates between the required arguments on the left and the unnecessary arguments on the right).[sup]1[\sup]
A simpler tactic to use if you don't want to deal with that is cbn which works here by default and works better in general. Quoting from the documentation:
The cbn tactic is claimed to be a more principled, faster and more predictable replacement for simpl.
Neither simpl with Arguments and a slash nor cbn reduce the goal to quite what you want in your case, since it'll unfold fold_length but not refold it. You could recognize that the call to fold is just fold_length l and refold it with fold (fold_length l).
Another possibility in your case is to use the change tactic. It seemed like you knew already that fold_length (a :: l) was supposed to simplify to S (fold_length l). If that's the case, you could use change (fold_length (a :: l)) with (S (fold_length l)). and Coq will try to convert one into the other (using only the basic conversion rules, not equalities like rewrite does).
After you've gotten the goal to S (fold_length l) = S (length l) using either of the above tactics, you can use rewrite -> IHl. like you wanted to.
I thought the slashes only made simpl unfold things less, which is why I didn't mention it before. I'm not sure what the default actually is, since putting the slash anywhere seems to make simpl unfold fold_length.
How do I prove a lemma like the following:
Require Import Coq.Lists.List.
Lemma len_seq_n : forall start n, length (seq start n)=n.
I tried
Proof.
induction n.
simpl. auto. simpl.
and at this point Coq gives me
1 subgoal
start, n : nat
IHn : length (seq start n) = n
______________________________________(1/1)
S (length (seq (S start) n)) = S n
I'm not sure how to proceed from there.
The problem is that your induction hypothesis is not general enough. You need the following statement instead:
IHn : forall start', length (seq start' n) = n
To obtain this hypothesis, simply generalize over start before doing induction on n with the revert tactic.
Proof.
intros start n.
revert start.
induction n.
(* Continue as previously *)
(Next time, please provide a complete example so that we can help you better. Your question was missing the definition of seq.)
When reasoning on paper, I often use arguments by induction on the length of some list. I want to formalized these arguments in Coq, but there doesn't seem to be any built in way to do induction on the length of a list.
How should I perform such an induction?
More concretely, I am trying to prove this theorem. On paper, I proved it by induction on the length of w. My goal is to formalize this proof in Coq.
There are many general patterns of induction like this one that can be covered
by the existing library on well founded induction. In this case, you can prove
any property P by induction on length of lists by using well_founded_induction, wf_inverse_image, and PeanoNat.Nat.lt_wf_0, as in the following comand:
induction l using (well_founded_induction
(wf_inverse_image _ nat _ (#length _)
PeanoNat.Nat.lt_wf_0)).
if you are working with lists of type T and proving a goal P l, this generates an
hypothesis of the form
H : forall y : list T, length y < length l -> P y
This will apply to any other datatype (like trees for instance) as long as you can map that other datatype to nat using any size function from that datatype to nat instead of length.
Note that you need to add Require Import Wellfounded. at the head of your development for this to work.
Here is how to prove a general list-length induction principle.
Require Import List Omega.
Section list_length_ind.
Variable A : Type.
Variable P : list A -> Prop.
Hypothesis H : forall xs, (forall l, length l < length xs -> P l) -> P xs.
Theorem list_length_ind : forall xs, P xs.
Proof.
assert (forall xs l : list A, length l <= length xs -> P l) as H_ind.
{ induction xs; intros l Hlen; apply H; intros l0 H0.
- inversion Hlen. omega.
- apply IHxs. simpl in Hlen. omega.
}
intros xs.
apply H_ind with (xs := xs).
omega.
Qed.
End list_length_ind.
You can use it like this
Theorem foo : forall l : list nat, ...
Proof.
induction l using list_length_ind.
...
That said, your concrete example example does not necessarily need induction on the length. You just need a sufficiently general induction hypothesis.
Import ListNotations.
(* ... some definitions elided here ... *)
Definition flip_state (s : state) :=
match s with
| A => B
| B => A
end.
Definition delta (s : state) (n : input) : state :=
match n with
| zero => s
| one => flip_state s
end.
(* ...some more definitions elided here ...*)
Theorem automata221: forall (w : list input),
extend_delta A w = B <-> Nat.odd (one_num w) = true.
Proof.
assert (forall w s, extend_delta s w = if Nat.odd (one_num w) then flip_state s else s).
{ induction w as [|i w]; intros s; simpl.
- reflexivity.
- rewrite IHw.
destruct i; simpl.
+ reflexivity.
+ rewrite <- Nat.negb_even, Nat.odd_succ.
destruct (Nat.even (one_num w)), s; reflexivity.
}
intros w.
rewrite H; simpl.
destruct (Nat.odd (one_num w)); intuition congruence.
Qed.
In case like this, it is often faster to generalize your lemma directly:
From mathcomp Require Import all_ssreflect.
Set Implicit Arguments.
Unset Strict Implicit.
Unset Printing Implicit Defensive.
Section SO.
Variable T : Type.
Implicit Types (s : seq T) (P : seq T -> Prop).
Lemma test P s : P s.
Proof.
move: {2}(size _) (leqnn (size s)) => ss; elim: ss s => [|ss ihss] s hs.
Just introduce a fresh nat for the size of the list, and regular induction will work.
I've been going through the Software Foundations course and found the following proof (source link).
Theorem not_exists_dist :
excluded_middle ->
forall (X:Type) (P : X -> Prop),
~ (exists x, ~ P x) -> (forall x, P x).
Proof.
unfold not. intros.
unfold excluded_middle in H.
assert ((P x) \/ ((P x) -> False)) as HP.
apply H with (P:=(P x)).
inversion HP.
apply H1.
apply ex_falso_quodlibet. apply H0. exists x. apply H1.
Qed.
I'm curious, why is there an assertion saying (P x) \/ ((P x) -> False), when if I unfold excluded_middle in H and unfold not in H, I'll get the exact same H : forall P : Prop, P \/ (P -> False) as the assertion, only that there's a universal quantifier.
This is even more obvious as the assertion can be proved just by doing apply H, and the whole reason for this step is to do inversion HP on the newly asserted hypothsesis.
The question is, why isn't it possible to do inversion H at the beginning directly, and spare the extra step of defining an assertion, which just copies one of the assumptions? Is there a better way to do this?
inversion only works on things of inductive type, such as or. forall is not an inductive type constructor, hence one cannot perform inversion on it. One could maybe extend inversion to behave like (e)destruct: if you give it something that is universally quantified, it'll generate additional existentials and proof obligations that you need to fulfill to fill in the missing spots, as well as destructing the conclusion. However, this is not how it works right now...
One could do a more direct proof by just applying H and destructing it directly:
Theorem not_exists_dist :
excluded_middle ->
forall (X:Type) (P : X -> Prop),
~ (exists x, ~ P x) -> (forall x, P x).
Proof.
intros.
destruct (H (P x)).
apply H1.
exfalso. apply H0. exists x. apply H1.
Qed.
I am trying to write an induction hypothesis specifically for proving properties of even numbers. I formulated and proved the following:
Theorem ind_hyp_on_evens:
forall (p : nat -> Prop),
(p 0 -> (forall n, p n -> p (S (S n))) ->
forall n, p (n + n)).
Proof.
intros p P0 P1.
intro n.
assert(p (n + n) /\ p (S (S (n + n)))).
induction n as [| n'].
split. unfold plus. assumption.
unfold plus.
apply (P1 0).
assumption.
destruct IHn' as [A B].
split.
rewrite <- plus_Snm_nSm.
rewrite -> ? plus_Sn_m.
assumption.
rewrite <- plus_Snm_nSm.
rewrite -> ? plus_Sn_m.
apply (P1 (S (S (n' + n')))).
assumption.
destruct H as [H1 H2].
assumption. Qed.
Despite the fact that it's proved, any attempt to use it results in the error message: "Error: Not the right number of induction arguments."
Can someone please tell me what is the problem with the induction hypothesis, or otherwise, how to apply it??
Thanks,
Mayer
I believe induction assumes that any induction principle that will be used has the
fixed form
forall ... (P : SomeType -> Type) ..., (* or ->Set or ->Prop *)
... ->
forall (v : SomeType), P v
Your ind_hyp_on_evens matches only P (plus n n) which seems to confuse induction.
If you have a suitable goal, say forall n, is_even (n+n), you can manually do the
steps that induction normally does and extend that to handle the special form.
intro n0; (* temp. var *)
pattern (n0 + n0); (* restructure as (fun x => (is_even x)) (n0+n0) *)
refine (ind_hyp_on_evens _ _ _ n0); (* apply ind. scheme *)
clear n0; [| intros n IHn ]. (* clear temp., do one 'intros' per branch *)
I don't know if it's possible to pack that up as a general helper tactic for any induction scheme, packing these steps up as a per-scheme Ltac tactic should work however.
You could consider writing an inductive predicate that describes even numbers (code not tested):
Inductive even : nat -> Prop :=
| evenO : even O
| evenSSn : forall n, even n -> even (S (S n))
.
Coq will generate the induction principle automatically.
You would have to prove that even n holds before being able to perform induction on the "evenness" of n.