Consider the following proposition in Coq:
Inductive subseq : list nat -> list nat -> Prop :=
| nil_s : forall (l: list nat), subseq nil l
| cons_in l1 l2 x (H: subseq l1 l2) : subseq (x :: l1) (x :: l2)
| cons_nin l1 l2 x (H: subseq l1 l2) : subseq l1 (x :: l2)
.
Lemma subseq_remove_rewritten: forall (x:nat) (l1' l1 l2 : list nat),
subseq l1' l2 ->
l1' = (x :: l1) ->
subseq l1 l2.
Proof.
intros x l1' l1 l2 H1 H2.
induction H1.
- discriminate.
- injection H2 as H3 H4.
rewrite H4 in H1.
apply cons_nin. apply H1.
- apply IHsubseq in H2.
apply cons_nin. apply H2.
Qed.
Lemma subseq_remove: forall (x:nat) (l1 l2 : list nat),
subseq (x :: l1) l2 ->
subseq l1 l2.
Proof.
intros x l1 l2 H.
apply subseq_remove_rewritten with (x:=x) (l1':=x :: l1).
apply H.
reflexivity.
Qed.
I worked in Isabelle before Coq. There originally, the induction tactic could not solve directly this goal and the trick was to come up with a lemma like subseq_remove_rewritten and then prove the original goal. This is the situation in the manual Isabelle/HOL: A Proof Assistant for Higher-Order Logic. Later, the tactic became smarter and one can write patterns in which to abstract on. So the proof is written like this:
lemma
assumes "subseq (x # l1) l2"
shows "subseq l1 l2"
using assms
apply(induction "x # l1" "l2" rule: subseq.induct)
apply simp
apply(intro subseq.intros(3),simp)
by (intro subseq.intros(3))
I was wondering if Coq has a similar way to avoid proving a lemma like subseq_remove_rewritten and go directly to prove subseq_remove.
You can use the dependent induction tactic (documented here). For example:
Require Import Coq.Lists.List.
Import ListNotations.
Require Import Coq.Program.Equality. (* Needed to use the tactic *)
Inductive subseq : list nat -> list nat -> Prop :=
| nil_s : forall (l: list nat), subseq nil l
| cons_in l1 l2 x (H: subseq l1 l2) : subseq (x :: l1) (x :: l2)
| cons_nin l1 l2 x (H: subseq l1 l2) : subseq l1 (x :: l2)
.
Lemma subseq_remove: forall (x:nat) (l1 l2 : list nat),
subseq (x :: l1) l2 ->
subseq l1 l2.
Proof.
intros x l1 l2 H.
dependent induction H generalizing x.
- now apply cons_nin.
- eauto using cons_nin.
Qed.
Unfortunately, though this tactic has been around for a while, it is still described as experimental in the reference manual, and I don't know if the Coq developers have any plans of improving it in the future. It has a few deficiencies, such as not allowing the user to name the variables and hypotheses used in the induction proof. I personally prefer to add the equality assumptions to the proof myself, as in your first attempt, or to reformulate the definition of subseq as a Fixpoint, so that you can invert the hypothesis by simplification. For example:
Require Import Coq.Lists.List.
Import ListNotations.
Fixpoint subseq (l1 l2 : list nat) : Prop :=
match l1, l2 with
| [], _ => True
| x1 :: l1, [] => False
| x1 :: l1, x2 :: l2 => x1 = x2 /\ subseq l1 l2 \/ subseq (x1 :: l1) l2
end.
Lemma subseq_nin x2 l1 l2 : subseq l1 l2 -> subseq l1 (x2 :: l2).
Proof. destruct l1 as [|x1 l1]; simpl; eauto. Qed.
Lemma subseq_remove: forall (x:nat) (l1 l2 : list nat),
subseq (x :: l1) l2 ->
subseq l1 l2.
Proof.
intros x l1 l2 H.
induction l2 as [|x2 l2 IH]; try easy.
destruct H as [[<- H]|H]; eauto using subseq_nin.
Qed.
Related
Disclaimer: This is not a homework question.
I am trying to implement my own version of rev_append in Coq, and then to prove that it is equivalent to the built in version. The following is my implementation.
Fixpoint my_rev_append (l1 l2 : list nat) : (list nat) * (list nat) :=
match l1 with
| nil => (l1, l2)
| hd :: tl => my_rev_append tl (hd :: l2)
end.
Then I tried to prove that it is equivalent to rev_append
Theorem my_rev_append_correct : forall (l1 l2 : list nat),
my_rev_append l1 l2 = (nil, (rev_append l1 l2)).
Proof.
intros l1 l2.
induction l1.
reflexivity.
And then I hit the following goal, which I do not see a way to move forward.
IHl1 : my_rev_append l1 l2 = (nil, rev_append l1 l2)
============================
my_rev_append (a :: l1) l2 = (nil, rev_append (a :: l1) l2)
It is not possible to use IHl1, because the RHS of the current subgoal is (nil, rev_append (a :: l1) l2), which does not contain (nil, rev_append l1 l2). I tried to run simpl tactic on it, but it didn't work, as IHl1 is still not applicable.
I totally understand that I can prove this by changing the | nil => (l1, l2) line in my_rev_append into | nil => l2. However, are there any possibility to prove this theorem without changing the definition of my_rev_append?
Your definition has l2 varying through the induction. Therefore, the proof of the theorem should also have l2 varying through the induction. To do this, do not introduce l2 before starting the induction, leaving it in the goal. The inductive hypothesis, whose type is modeled on this goal, then allows you to pass a different value for it in the recursive case.
Theorem my_rev_append_correct : forall (l1 l2 : list nat), my_rev_append l1 l2 = (nil, rev_append l1 l2).
Proof.
induction l1 as [ | x l1 rec]; intros l2.
- reflexivity.
- apply rec.
Qed.
I'm new in coq. i am trying to prove that the subsequence of an empty list is empty
This is the lemma i'm working on:
Lemma sub_nil : forall l , subseq l nil <-> l=nil.
i tried to split so i can have
subseq l nil -> l = nil
and
l = nil -> subseq l nil
to prove the first one i tried an induction on l but i blocked when it comes to prove that
subseq (a :: l) nil -> a :: l = nil
thanks.
The tactic to use here is inversion. Paraphrasing the coq documentation for inversion! :
Given an inductive hypothesis (H:I t), then inversion applied to H derives for each possible constructor c i of (I t), all the necessary conditions that should hold for the instance (I t) to be proved by c i.
Assuming the subseq predicate is given as follows:
Inductive subseq {A:Type} : list A -> list A -> Prop :=
| SubNil : forall (l:list A), subseq nil l
| SubCons1 : forall (s l:list A) (x:A), subseq s l -> subseq s (x::l)
| SubCons2 : forall (s l: list A) (x:A), subseq s l -> subseq (x::s) (x::l).
The proof would be stuck here(exactly at the place you specified):
Lemma sub_nil2 : forall (A:Type) (l: list A) , subseq l nil <-> l=nil.
Proof.
split.
- destruct l eqn:E; intros.
* reflexivity.
(*Now unable to prove a::l0 = [] because the hypothesis: subseq (a :: l0) [] is absurd.*)
* inversion H.(*Coq reasons that this hypothesis is not possible and discharges the proof trivially*)
- intros. subst. apply SubNil.
Qed.
Note that I used the destruct tactic but the issue remains even with induction tactic.
The entire proof can be written cleanly as below:
Lemma sub_nil : forall (A:Type) (l: list A) , subseq l nil <-> l=nil.
Proof.
split; intros.
- inversion H. reflexivity.
- subst. apply SubNil.
Qed.
Following my question here, I have a function findshare which finds the same elements in two lists. Actually, keepnotEmpty is the lemma I need in my program after applying some changes to the initial version of lemma sameElements. Lemma keepnotEmpty proves if the result of function findshare on the concatenation of two lists is not empty then the concatenation of the results of the function applied to each one of them is not empty as well. I'm confused how to prove lemma keepnotEmpty. Thank you.
Require Import List .
Import ListNotations.
Fixpoint findshare(s1 s2: list nat): list nat:=
match s1 with
| nil => nil
| v :: tl =>
if ( existsb (Nat.eqb v) s2)
then v :: findshare tl s2
else findshare tl s2
end.
Lemma sameElements l1 l2 tl :
(findshare(l1++l2) tl) =
(findshare l1 tl) ++ (findshare l2 tl ).
Proof.
Admitted.
Lemma keepnotEmpty l1 l2 tl :
(findshare tl (l1++l2)) <> nil -> (findshare tl (l1) ++ (findshare tl (l2))<>nil).
Proof.
You need induction on tl and the property oneNotEmpty of lists to prove lemmakeepnotEmpty.
Lemma oneNotEmpty (l1 l2:list nat):
l1<>nil -> (l2++l1)<>nil.
Proof.
Admitted.
Lemma keepnotEmpty l1 l2 tl :
(findshare tl (l1++l2))<> nil -> (findshare tl (l1) ++ (findshare tl (l2))<>nil).
Proof.
induction tl. simpl; intro. congruence.
simpl.
rewrite existsb_app.
destruct_with_eqn(existsb (Nat.eqb a) l1).
destruct_with_eqn(existsb (Nat.eqb a) l2);
simpl; intros H1 H2; congruence.
destruct_with_eqn(existsb (Nat.eqb a) l2).
simpl. intros. apply (oneNotEmpty);
intro. inversion H0.
simpl; assumption.
Qed.
I'm currently working through the Logical Foundations book and I'm stuck on the last part of Exercise: 4 stars, advanced (subsequence) (subseq_trans).
Here is my definition for subseq:
Inductive subseq { X : Type } : list X -> list X -> Prop :=
| s1 : forall l, subseq [] l
| s2 : forall (x : X) (l l': list X), subseq l l' -> subseq l (x :: l')
| s3 : forall (x : X) (l l' : list X), subseq l l' -> subseq (x :: l) (x :: l').
And here is my proof for subseq_trans:
Theorem subseq_trans : forall (X : Type) (l1 l2 l3 : list X),
subseq l1 l2 -> subseq l2 l3 -> subseq l1 l3.
Proof.
intros X l1 l2 l3 H H'.
generalize dependent H.
generalize dependent l1.
induction H'.
{ intros l1 H. inversion H. apply s1. }
{ intros l1 H. apply s2. apply IHH'. apply H. }
{ intros l1 H. apply s2. apply IHH'. apply s2 in H. (* Unable to find an instance for the variable x. *) }
Here is the proof context before the failed apply:
1 subgoal
X : Type
x : X
l, l' : list X
H' : subseq l l'
IHH' : forall l1 : list X, subseq l1 l -> subseq l1 l'
l1 : list X
H : subseq l1 (x :: l)
______________________________________(1/1)
subseq l1 l
I have tried explicitly instantiating x like this:
apply s2 with (x:=x) in H
But that gives me:
No such bound variable x (possible names are: x0, l0 and l'0).
Thanks in advance.
As diagnosed by #tbrk, this is a renaming done by Coq in the presence of maximal implicit arguments (see this issue). This is due to the declaration of {X : Type} in the definition of subsequence.
One solution is to use # to turn all implicit arguments to non-implicit and avoid this renaming issue. This would give:
apply #s2 with (x:=x) in H.
You may find the eapply tactic useful to see what is going on.
...
{ intros l1 H. apply s2. apply IHH'. eapply s2 in H.
gives subseq l1 (?1 :: x :: l), where you can instantiate the ?1 with whatever you want, but, as you can now see, applying s2 forward from that assumption doesn't advance the proof.
Another possibility is to apply s2 to x and then to the assumption H:
apply (s2 x) in H.
I also find it strange that apply s2 with (x:=x) does not work. Coq seems to be doing some renaming behind the scenes, probably to avoid confusion with the x in the proof context. The following sequence applies without error:
rename x into y. apply s2 with (x:=y) in H.
I have the defined inductive types:
Inductive InL (A:Type) (y:A) : list A -> Prop :=
| InHead : forall xs:list A, InL y (cons y xs)
| InTail : forall (x:A) (xs:list A), InL y xs -> InL y (cons x xs).
Inductive SubSeq (A:Type) : list A -> list A -> Prop :=
| SubNil : forall l:list A, SubSeq nil l
| SubCons1 : forall (x:A) (l1 l2:list A), SubSeq l1 l2 -> SubSeq l1 (x::l2)
| SubCons2 : forall (x:A) (l1 l2:list A), SubSeq l1 l2 -> SubSeq (x::l1) (x::l2).
Now I have to prove a series of properties of that inductive type, but I keep getting stuck.
Lemma proof1: forall (A:Type) (x:A) (l1 l2:list A), SubSeq l1 l2 -> InL x l1 -> InL x l2.
Proof.
intros.
induction l1.
induction l2.
exact H0.
Qed.
Can some one help me advance.
In fact, it is easier to do an induction on the SubSet judgment directly.
However, you need to be as general as possible, so here is my advice:
Lemma proof1: forall (A:Type) (x:A) (l1 l2:list A),
SubSeq l1 l2 -> InL x l1 -> InL x l2.
(* first introduce your hypothesis, but put back x and In foo
inside the goal, so that your induction hypothesis are correct*)
intros.
revert x H0. induction H; intros.
(* x In [] is not possible, so inversion will kill the subgoal *)
inversion H0.
(* here it is straitforward: just combine the correct hypothesis *)
apply InTail; apply IHSubSeq; trivial.
(* x0 in x::l1 has to possible sources: x0 == x or x0 in l1 *)
inversion H0; subst; clear H0.
apply InHead.
apply InTail; apply IHSubSeq; trivial.
Qed.
"inversion" is a tactic that checks an inductive term and gives you all the possible way to build such a term !!without any induction hypothesis!!
It only gives you the constructive premices.
You could have done it directly by induction on l1 then l2, but you would have to construct by hand the correct instance of inversion because your induction hypothesis would have been really weak.
Hope it helps,
V.