Take a conjunction of two hypotheses and create a new hypothesis in Coq - coq

I'm wondering as destruct H as (H1 & H2). on hypothesis H : p /\ q creates two hypotheses H1 : p and H2 : q, if there is any tactic that does the other way around. That is, taking two hypotheses and creating one with conjunction of those.

Here are two possibilities, using the assert tactic:
Goal forall A B : Prop, A -> B -> A /\ B.
Proof.
intros A B HA HB.
assert (H1 : A /\ B). { now split. }
assert (H2 := conj HA HB).
Abort.

Related

Rewrite under exists

Say I have the following relation:
Inductive my_relation: nat -> Prop :=
constr n: my_relation n.
and I want to prove the following:
Lemma example:
(forall n, my_relation n -> my_relation (S n)) -> (exists n, my_relation n) -> exists n, my_relation (S n).
Proof.
intros.
After introducing, I have the following environment:
1 subgoal
H : forall n : nat, my_relation n -> my_relation (S n)
H0 : exists n : nat, my_relation n
______________________________________(1/1)
exists n : nat, my_relation (S n)
My question is: is there a possibility to rewrite H under the exists quantifier ? If not, is there a strategy to solve this kind of problem (this particular one is not really relevant, but problems where you have to prove an exists using another exists, and where, informally, you can « deduce » a way to rewrite the exists in the hypothesis into the exists in the goal) ?
For instance, if I try rewrite H in H0. I have, an error (Error: Cannot find a relation to rewrite.).
The standard way to manipulate an existential quantification in an hypothesis is to get a witness of the property using inversion or, better and simpler, destruct.
You can give a name to the variable using one of the following syntaxes:
destruct H0 as (n, H0).
destruct H0 as [n H0].
destruct H0 as (n & H0).
Note that you can also destruct an hypothesis using intro-patterns.
intros H (n & H0).
And you can even directly apply H in H0.
intros H (n & H0%H). exists n. assumption.
Software Foundations explains this in a clear way.
I found a way, I post it here for any similar questions in the future.
It is possible to inverse the exists hypothesis, in order to "instantiate" the quantified variable, for instance, here, the proof can be finished by:
inversion H0.
apply H in H1.
exists x.
apply H1.
Qed.
After inversion H0, we have in the environment:
1 subgoal
H : forall n : nat, my_relation n -> my_relation (S n)
H0 : exists n : nat, my_relation n
x : nat
H1 : my_relation x
______________________________________(1/1)
exists n : nat, my_relation (S n)
and we can now work with x.

How to make use of a hypothesis containing forall in Coq?

I am trying to prove the equivalence of P \/ Q and ~ P -> Q, under the assumption of Excluded Middle,
Theorem eq_of_or :
excluded_middle ->
forall P Q : Prop,
(P \/ Q) <-> (~ P -> Q).
where the Excluded Middle is the following.
Definition excluded_middle := forall P : Prop, P \/ ~ P.
Actually, the proof of one direction does not require the Excluded Middle. In my attempt at proving the other direction, I get stuck when I am trying to utilize the Excluded Middle among the hypotheses,
Proof.
intros EM P Q. split.
{ intros [H | H]. intros HNP.
- unfold not in HNP. exfalso.
apply HNP. apply H.
- intros HNP. apply H. }
{ intros H. unfold excluded_middle in EM.
unfold not in EM. unfold not in H.
}
where the current environment is the following:
1 subgoal
EM : forall P : Prop, P \/ (P -> False)
P, Q : Prop
H : (P -> False) -> Q
______________________________________(1/1)
P \/ Q
I understand that under such circumstance, what we need to do next is to do something like the "case analysis" of P, including the use of tactics left and right, if my proof makes sense till now.
Thanks in advance for any advice and suggestion!
You can instantiate EM : forall P : Prop, P \/ ~ P with any proposition (I instantiated it with P below and destructed it immediately), since
EM is essentially a function that takes an arbitrary proposition P and returns a proof of either P or ~ P.
Theorem eq_of_or' :
excluded_middle ->
forall P Q : Prop, (~ P -> Q) -> P \/ Q.
Proof.
intros EM P Q.
destruct (EM P) as [p | np]. (* <- the key part is here *)
- left. apply p.
- right.
apply (H np).
(* or, equivalently, *)
Undo.
apply H.
apply np.
Undo 2.
(* we can also combine two `apply` into one: *)
apply H, np.
Qed.

How to do induction differently?

I am doing an exercise in Coq and trying to prove if a list equals to its reverse, it's a palindrome. Here is how I define palindromes:
Inductive pal {X : Type} : list X -> Prop :=
| emptypal : pal []
| singlpal : forall x, pal [x]
| inducpal : forall x l, pal l -> pal (x :: l ++ [x]).
Here is the theorem:
Theorem palindrome3 : forall {X : Type} (l : list X),
l = rev l -> pal l.
According to my definition, I will need to do the induction my extracting the front and tail element but apparently coq won't let me do it, and if I force it to do so, it gives an induction result that definitely doesn't make any sense:
Proof.
intros X l H. remember (rev l) as rl. induction l, rl.
- apply emptypal.
- inversion H.
- inversion H.
- (* stuck *)
context:
1 subgoals
X : Type
x : X
l : list X
x0 : X
rl : list X
Heqrl : x0 :: rl = rev (x :: l)
H : x :: l = x0 :: rl
IHl : x0 :: rl = rev l -> l = x0 :: rl -> pal l
______________________________________(1/1)
pal (x :: l)
aparently the inductive context is terribly wrong. is there any way I can fix the induction?
The solution I propose here is probably not the shortest one, but I think it is rather natural.
My solution consists in defining an induction principle on list specialized to your problem.
Consider natural numbers. There is not only the standard induction nat_ind where you prove P 0 and forall n, P n -> P (S n). But there are other induction schemes, e.g., the strong induction lt_wf_ind, or the two-step induction where you prove P 0, P 1 and forall n, P n -> P (S (S n)). If the standard induction scheme is not strong enough to prove the property you want, you can try another one.
We can do the same for lists. If the standard induction scheme list_ind is not enough, we can write another one that works. In this idea, we define for lists an induction principle similar to the two-step induction on nat (and we will prove the validity of this induction scheme using the two-step induction on nat), where we need to prove three cases: P [], forall x, P [x] and forall x l x', P l -> P (x :: l ++ [x']). The proof of this scheme is the difficult part. Applying it to deduce your theorem is quite straightforward.
I don't know if the two-step induction scheme is part of the standard library, so I introduce it as an axiom.
Axiom nat_ind2 : forall P : nat -> Prop, P 0 -> P 1 ->
(forall n : nat, P n -> P (S (S n))) -> forall n : nat, P n.
Then we prove the induction scheme we want.
Lemma list_ind2 : forall {A} (P : list A -> Prop) (P_nil : P [])
(P_single : forall x, P [x])
(P_cons_snoc : forall x l x', P l -> P (x :: l ++ [x'])),
forall l, P l.
Proof.
intros. remember (length l) as n. symmetry in Heqn. revert dependent l.
induction n using nat_ind2; intros.
- apply length_zero_iff_nil in Heqn. subst l. apply P_nil.
- destruct l; [discriminate|]. simpl in Heqn. inversion Heqn; subst.
apply length_zero_iff_nil in H0. subst l. apply P_single.
- destruct l; [discriminate|]. simpl in Heqn.
inversion Heqn; subst. pose proof (rev_involutive l) as Hinv.
destruct (rev l). destruct l; discriminate. simpl in Hinv. subst l.
rewrite app_length in H0.
rewrite PeanoNat.Nat.add_comm in H0. simpl in H0. inversion H0.
apply P_cons_snoc. apply IHn. assumption.
Qed.
You should be able to conclude quite easily using this induction principle.
Theorem palindrome3 : forall {X : Type} (l : list X),
l = rev l -> pal l.

Combining two Coq hypotheses

So I have two hypotheses, one that is h : A -> B, and the other which is h2 : A. How can I get h3 : B to appear in my hypotheses?
pose proof (h h2) as h3.
introduces h3 : B as a new hypothesis,
specialize (h h2).
modifies h : A -> B into h : B -- this can be useful if you won't need h later, and symmetrically,
apply h in h2.
converts h2 : A into h2 : B.
Another (not very convenient) way would be to
assert B as h3 by exact (h h2).
That's what the pose proof variant is equivalent to.
Also, in a simple case like the following, you can solve your goal without introducing a new hypothesis:
Goal forall (A B : Prop), (A -> B) -> A -> B.
intros A B h h2.
apply (h h2).
Qed.

Induction on predicates with product type arguments

If I have a predicate like this:
Inductive foo : nat -> nat -> Prop :=
| Foo : forall n, foo n n.
then I can trivially use induction to prove some dummy lemmas:
Lemma foo_refl : forall n n',
foo n n' -> n = n'.
Proof.
intros.
induction H.
reflexivity.
Qed.
However, for a predicate with product type arguments:
Inductive bar : (nat * nat) -> (nat * nat) -> Prop :=
| Bar : forall n m, bar (n, m) (n, m).
a similar proof for nearly identical lemma gets stuck because all assumptions about variables disappear:
Lemma bar_refl : forall n n' m m',
bar (n, m) (n', m') -> n = n'.
Proof.
intros.
induction H.
(* :( *)
Why is this happening? If I replace induction with inversion, then it behaves as expected.
The lemma is still provable with induction but requires some workarounds:
Lemma bar_refl : forall n n' m m',
bar (n, m) (n', m') -> n = n'.
Proof.
intros.
remember (n, m) as nm.
remember (n', m') as n'm'.
induction H.
inversion Heqnm. inversion Heqn'm'. repeat subst.
reflexivity.
Qed.
Unfortunately, this way proofs gets completely cluttered and are impossible to follow for more complicated predicates.
One obvious solution would be to declare bar like this:
Inductive bar' : nat -> nat -> nat -> nat -> Prop :=
| Bar' : forall n m, bar' n m n m.
This solves all the problems. Yet, for my purposes, I find the previous ("tupled") approach somewhat more elegant. Is there a way to keep the predicate as it is and still be able to do manageable inductive proofs? Where does the problem even come from?
The issue is that induction can only works with variables, not constructed terms. This is why you should first prove something like
Lemma bar_refl : forall p q, bar p q -> fst p = fst q.
which is trivially proved by now induction 1. to prove your lemma.
If you don't want the intermediate lemma to have a name, your solution is the correct one: you need to help Coq with remember to generalize your goal, and then you'll be able to prove it.
I don't remember exactly where this restriction comes from, but I recall something about making some unification problem undecidable.
Often in these situation one can do induction on one of the sub-terms.
In your case your lemma can be proved by induction on n, with
Lemma bar_refl : forall n n' m m', bar (n, m) (n', m') -> n = n'.
Proof. induction n; intros; inversion H; auto. Qed.
... all assumptions about variables disappear... Why is this happening? If I replace induction with inversion, then it behaves as expected.
The reason that happens is described perfectly in this blog post:
Dependent Case Analysis in Coq without Axioms
by James Wilcox. Let me quote the most relevant part for this case:
When Coq performs a case analysis, it first abstracts over all indices. You may have seen this manifest as a loss of information when using destruct on predicates (try destructing even 3 for example: it just deletes the hypothesis!), or when doing induction on a predicate with concrete indices (try proving forall n, even (2*n+1) -> False by induction on the hypothesis (not the nat) -- you'll be stuck!). Coq essentially forgets the concrete values of the indices. When trying to induct on such a hypothesis, one solution is to replace each concrete index with a new variable together with a constraint that forces the variable to be equal to the correct concrete value. destruct does something similar: when given a term of some inductive type with concrete index values, it first replaces the concrete values with new variables. It doesn't add the equality constraints (but inversion does). The error here is about abstracting out the indices. You can't just go replacing concrete values with arbitrary variables and hope that things still type check. It's just a heuristic.
To give a concrete example, when using destruct H. one basically does pattern-matching like so:
Lemma bar_refl : forall n n' m m',
bar (n, m) (n', m') -> n = n'.
Proof.
intros n n' m m' H.
refine (match H with
| Bar a b => _
end).
with the following proof state:
n, n', m, m' : nat
H : bar (n, m) (n', m')
a, b : nat
============================
n = n'
To get almost the exact proof state we should've erased H from the context, using the clear H. command: refine (...); clear H.. This rather primitive pattern-matching doesn't allow us to prove our goal.
Coq abstracted away (n, m) and (n',m') replacing them with some pairs p and p', such that p = (a, b) and p' = (a, b). Unfortunately, our goal has the form n = n' and there is neither (n,m) nor (n',m') in it -- that's why Coq didn't change the goal with a = a.
But there is a way to tell Coq to do that. I don't know how to do exactly that using tactics, so I'll show a proof term. It's is going to look somewhat similar to #Vinz's solution, but notice that I didn't change the statement of the lemma:
Undo. (* to undo the previous pattern-matching *)
refine (match H in (bar p p') return fst p = fst p' with
| Bar a b => _
end).
This time we added more annotations for Coq to understand the connections between the components of H's type and the goal -- we explicitly named the p and p' pairs and because we told Coq to treat our goal as fst p = fst p' it will replace p and p' in the goal with (a,b). Our proof state looks like this now:
n, n', m, m' : nat
H : bar (n, m) (n', m')
a, b : nat
============================
fst (a, b) = fst (a, b)
and simple reflexivity is able to finish the proof.
I think now it should be clear why destruct works fine in the following lemma (don't look at the answer below, try to figure it out first):
Lemma bar_refl_all : forall n n' m m',
bar (n, m) (n', m') -> (n, m) = (n', m').
Proof.
intros. destruct H. reflexivity.
Qed.
Answer: because the goal contains the same pairs that are present in the hypothesis's type, so Coq replaces them all with appropriate variables and that will prevent the information loss.
Another way ...
Lemma bar_refl n n' m m' : bar (n, m) (n', m') -> n = n'.
Proof.
change (n = n') with (fst (n,m) = fst (n',m')).
generalize (n,m) (n',m').
intros ? ? [ ]; reflexivity.
Qed.