Automatically specialize forall when the parameters are in scope - coq

Consider the following simple problem:
Goal forall (R : relation nat) (a b c d e f g h : nat),
(forall m n : nat, R m n -> False) -> (R a b) -> False.
Proof.
intros ? a b c d e f g h H1 H2.
saturate H1. (* <-- TODO implement this *)
assumption.
Qed.
My current implementation of saturate instantiates H1 with every possible combination of nat hypotheses, leading to quadratic blowup in time and memory usage. Instead I would like it to inspect forall and see that it requires a R m n, so the only combination of parameters that makes sense in context is a and then b.
Is there a known solution to this? My intuition is to use evars, but if I could avoid them without sacrificing significant performance I would like to.

This might be too simplistic, the idea is to try apply H1 on every hypothesis:
Ltac saturate H :=
match goal with
(* For any hypothesis I... *)
| [ I : _ |- _ ] =>
(* 1. Clone it (to not lose information). *)
let J := fresh I in pose proof I as J;
(* 2. apply H. *)
apply H in J;
(* 3. Abort if we already knew the resulting fact. *)
let T := type of J in
match goal with
| [ I1 : T, I2 : T |- _ ] => fail 2
end + idtac;
(* 4. Keep going *)
try (saturate H)
end.
Example:
Require Setoid. (* To get [relation] in scope so the example compiles *)
(* Made the example a little less trivial to better test the backtracking logic. *)
Goal forall (R S : relation nat) (a b c d e f g h : nat),
(forall m n : nat, R m n -> S m n) -> (R a b) -> R c d -> S a b.
Proof.
intros R S a b c d e f g h H1 H2 H3.
(* --- BEFORE ---
H2: R a b
H3: R c d
*)
saturate H1.
(* --- AFTER ---
H2: R a b
H3: R c d
H0: S c d
H4: S a b
*)
assumption.
Qed.

Related

Don't understand `destruct` tactic on hypothesis `~ (exists x : X, ~ P x)` in Coq

I'm new to Coq and try to learn it through Software foundations. In the chapter "Logic in Coq", there is an exercise not_exists_dist which I completed (by guessing) but not understand:
Theorem not_exists_dist :
excluded_middle →
∀ (X:Type) (P : X → Prop),
¬ (∃ x, ¬ P x) → (∀ x, P x).
Proof.
intros em X P H x.
destruct (em (P x)) as [T | F].
- apply T.
- destruct H. (* <-- This step *)
exists x.
apply F.
Qed.
Before the destruct, the context and goal looks like:
em: excluded_middle
X: Type
P: X -> Prop
H: ~ (exists x : X, ~ P x)
x: X
F: ~ P x
--------------------------------------
(1/1)
P x
And after it
em: excluded_middle
X: Type
P: X -> Prop
x: X
F: ~ P x
--------------------------------------
(1/1)
exists x0 : X, ~ P x0
While I understand destruct on P /\ Q and P \/ Q in hypothesis, I don't understand how it works on P -> False like here.
Let me try to give some intuition behind this by doing another proof first.
Consider:
Goal forall A B C : Prop, A -> C -> (A \/ B -> B \/ C -> A /\ B) -> A /\ B.
Proof.
intros. (*eval up to here*)
Admitted.
What you will see in *goals* is:
1 subgoal (ID 77)
A, B, C : Prop
H : A
H0 : C
H1 : A ∨ B → B ∨ C → A ∧ B
============================
A ∧ B
Ok, so we need to show A /\ B. We can use split to break the and apart, thus we need to show A and B. A follows easily by assumption, B is something we do not have. So, our proof script now might look like:
Goal forall A B C : Prop, A -> C -> (A \/ B -> B \/ C -> A /\ B) -> A /\ B.
Proof.
intros. split; try assumption. (*eval up to here*)
Admitted.
With goals:
1 subgoal (ID 80)
A, B, C : Prop
H : A
H0 : C
H1 : A ∨ B → B ∨ C → A ∧ B
============================
B
The only way we can get to the B is by somehow using H1. Let's see what destruct H1 does to our goals:
3 subgoals (ID 86)
A, B, C : Prop
H : A
H0 : C
============================
A ∨ B
subgoal 2 (ID 87) is:
B ∨ C
subgoal 3 (ID 93) is:
B
We get additional subgoals! In order to destruct H1 we need to provide it proofs for A \/ B and B \/ C, we cannot destruct A /\ B otherwise!
For the sake of completeness: (without the split;try assumption shorthand)
Goal forall A B C : Prop, A -> C -> (A \/ B -> B \/ C -> A /\ B) -> A /\ B.
Proof.
intros. split.
- assumption.
- destruct H1.
+ left. assumption.
+ right. assumption.
+ assumption.
Qed.
Another way to view it is this: H1 is a function that takes A \/ B and B \/ C as input. destruct works on its output. In order to destruct the result of such a function, you need to give it an appropriate input.
Then, destruct performs a case analysis without introducing additional goals.
We can do that in the proof script as well before destructing:
Goal forall A B C : Prop, A -> C -> (A \/ B -> B \/ C -> A /\ B) -> A /\ B.
Proof.
intros. split.
- assumption.
- specialize (H1 (or_introl H) (or_intror H0)).
destruct H1.
assumption.
Qed.
From a proof term perspective, destruct of A /\ B is the same as match A /\ B with conj H1 H2 => (*construct a term that has your goal as its type*) end.
We can replace the destruct in our proof script with a corresponding refine that does exactly that:
Goal forall A B C : Prop, A -> C -> (A \/ B -> B \/ C -> A /\ B) -> A /\ B.
Proof.
intros. unfold not in H0. split.
- assumption.
- specialize (H1 (or_introl H) (or_intror H0)).
refine (match H1 with conj Ha Hb => _ end).
exact Hb.
Qed.
Back to your proof. Your goals before destruct
em: excluded_middle
X: Type
P: X -> Prop
H: ~ (exists x : X, ~ P x)
x: X
F: ~ P x
--------------------------------------
(1/1)
P x
After applying the unfold not in H tactic you see:
em: excluded_middle
X: Type
P: X -> Prop
H: (exists x : X, P x -> ⊥) -> ⊥
x: X
F: ~ P x
--------------------------------------
(1/1)
P x
Now recall the definition of ⊥: It's a proposition that cannot be constructed, i.e. it has no constructors.
If you somehow have ⊥ as an assumption and you destruct, you essentially look at the type of match ⊥ with end, which can be anything.
In fact, we can prove any goal with it:
Goal (forall (A : Prop), A) <-> False. (* <- note that this is different from *)
Proof. (* forall (A : Prop), A <-> False *)
split; intros.
- specialize (H False). assumption.
- refine (match H with end).
Qed.
Its proofterm is:
(λ (A B C : Prop) (H : A) (H0 : C) (H1 : A ∨ B → B ∨ C → A ∧ B),
conj H (let H2 : A ∧ B := H1 (or_introl H) (or_intror H0) in match H2 with
| conj _ Hb => Hb
end))
Anyhow, destruct on your assumption H will give you a proof for your goal if you are able to show exists x : X, ~ P x -> ⊥.
Instead of destruct, you could also do exfalso. apply H. to achieve the same thing.
Normally, destruct t applies when t is an inhabitant of an inductive type I, giving you one goal for each possible constructor for I that could have been used to produce t. Here as you remarked H has type P -> False, which is not an inductive type, but False is. So what happens is this: destruct gives you a first goal corresponding to the P hypothesis of H. Applying H to that goal leads to a term of type False, which is an inductive type, on which destruct works as it should, giving you zero goals since False has no constructors. Many tactics for inductive types work like this on hypothesis of the form P1 -> … -> Pn -> I where I is an inductive type: they give you side-goals for P1 … Pn, and then work on I.

Logic: All_In can't expand nested forall

I am facing a pretty strange problem: coq doesn't want to move forall variable into the context.
In the old times it did:
Example and_exercise :
forall n m : nat, n + m = 0 -> n = 0 /\ m = 0.
Proof.
intros n m.
It generates:
n, m : nat
============================
n + m = 0 -> n = 0 /\ m = 0
But when we have forall inside forall, it doesn't work:
(* Auxilliary definition *)
Fixpoint All {T : Type} (P : T -> Prop) (l : list T) : Prop :=
(* ... *)
Lemma All_In :
forall T (P : T -> Prop) (l : list T),
(forall x, In x l -> P x) <->
All P l.
Proof.
intros T P l. split.
- intros H.
After this we get:
T : Type
P : T -> Prop
l : list T
H : forall x : T, In x l -> P x
============================
All P l
But how to move x outside of H and destruct it into smaller pieces? I tried:
destruct H as [x H1].
But it gives an error:
Error: Unable to find an instance for the variable x.
What is it? How to fix?
The problem is that forall is nested to the left of an implication rather than the right. It does not make sense to introduce x from a hypothesis of the form forall x, P x, just like it wouldn't make sense to introduce the n in plus_comm : forall n m, n + m = m + n into the context of another proof. Instead, you need to use the H hypothesis by applying it at the right place. I can't give you the answer to this question, but you might want to refer to the dist_not_exists exercise in the same chapter.

Attempting to use proof irrelevance without creating ill-typed terms

To illustrate the issue I am facing let us assume we have a predicate on nat:
Parameter pred : nat -> Prop
Let us assume further that we have a type which encapsulates data, as well as a proof that the encapsulated data satisfies a certain property. For example:
Inductive obj : Type :=
| c : forall (n:nat), pred n -> obj
.
Now we would like to regard two objects c n p and c m q to be the same objects as long as n = m, regardless of the proofs involved to build them. So let us introduce a proof irrelevance axiom:
Axiom irrel : forall (P:Prop) (p q:P), p = q.
Now given this axiom, it is expected that the equality c n p = c m q be provable for n = m :
Theorem obvious : forall (n m:nat) (p: pred n) (q:pred m),
n = m -> c n p = c m q.
Now I have been playing around with this for a while, and none of the typical 'rewrite' tactics can work as they create ill-typed terms. I am guessing the theorem should be true within Coq's type theory (given the proof irrelevance axiom) but probably involves some trick unknown to a beginner. Any suggestion is greatly appreciated.
TL;DR
Theorem obvious n m (p: pred n) (q: pred m) :
n = m -> c n p = c m q.
Proof.
intros ->.
rewrite (irrel _ p q).
reflexivity.
Qed.
Explanation
Let me show how one can use information containing in error messages to come up with a solution:
Theorem obvious n m (p: pred n) (q: pred m) :
n = m -> c n p = c m q.
Proof.
intros E.
Fail rewrite E.
At this point we get the following error message:
The command has indeed failed with message:
Abstracting over the term "n" leads to a term fun n0 : nat => c n0 p = c m q
which is ill-typed.
Reason is: Illegal application:
The term "c" of type "forall n : nat, pred n -> obj"
cannot be applied to the terms
"n0" : "nat"
"p" : "pred n"
The 2nd term has type "pred n" which should be coercible to "pred n0".
The rewrite tactic tried to build the proof term using eq_ind_r lemma. Let us look at its type:
eq_ind_r
: forall (A : Type) (x : A) (P : A -> Prop),
P x -> forall y : A, y = x -> P y
rewrite tries to build the following term:
#eq_ind_r _ m (fun x => c x p = c m q) (subgoal : c m p = c m q) n E.
which is ill-typed:
Fail Check #eq_ind_r _ m (fun x => c x p = c m q).
The term "p" has type "pred n" while it is expected to have type "pred x".
This means that the link between n and pred n has been lost at this point and we can restore it by saying explicitly that x and p must comply with each other by generalizing over p:
Check #eq_ind_r _ m (fun x => forall (p : pred x), c x p = c m q).
The above means we can proceed to finish the proof in the following manner:
revert p.
rewrite H; intros p.
rewrite (irrel _ p q).
reflexivity.
Qed.
The original version of the code uses intro-pattern intros -> to achieve the effect of the longer intros E; revert p; rewrite E; intros p. for this particular case.

Getting a stronger induction principle in Coq

Assume the following:
Inductive bin : Set := Z | O.
Fixpoint fib (n : nat) : list bin :=
match n with
| 0 => [Z]
| S k => match k with
| 0 => [O]
| S k' => fib k' ++ fib k
end
end.
I would like to show:
Theorem fib_first : forall n,
Nat.Even n -> n > 3 -> exists w, fib n = Z :: w.
However, by performing induction on n, I get a really useless inductive
hypothesis fixing n, stating that IH : Nat.Even n -> n > 3 -> exists w : list bin, fib n = Z :: w.
What I would ideally have is the following: IH : forall n : nat, Nat.Even n -> n > 3 -> exists w : list bin, fib n = Z :: w. Naturally I cannot assume the original proposition, but it feels like I need to prove something stronger perhaps?
My idea for the inductive reasoning would be made possible by expanding F n = F n-2 . F n-1, we know F n-2 is even iff F n is even, and since neither of F n-2 or F n-1 is empty, we can show the substring is shorter, therefore sufficient for the inductive hypothesis - how does one express this in Coq?
The trick is to unfold the definition of Nat.Even and do induction on n / 2 instead of n:
Theorem fib_first : forall n,
Nat.Even n -> exists w, fib n = Z :: w.
Proof.
intros n [m ->].
induction m as [|m IH].
- now exists nil.
- rewrite <- mult_n_Sm, plus_comm.
generalize (2 * m) IH. clear m IH. simpl.
intros n [w ->].
simpl. eauto.
Qed.
Note that your n > 3 hypothesis is not actually needed.

Convert ~exists to forall in hypothesis

I'm stuck in situation where I have hypothesis ~ (exists k, k <= n+1 /\ f k = f (n+2)) and wish to convert it into equivalent (I hope so) hypothesis forall k, k <= n+1 -> f k <> f (n+2).
Here is little example:
Require Import Coq.Logic.Classical_Pred_Type.
Require Import Omega.
Section x.
Variable n : nat.
Variable f : nat -> nat.
Hypothesis Hf : forall i, f i <= n+1.
Variable i : nat.
Hypothesis Hi : i <= n+1.
Hypothesis Hfi: f i = n+1.
Hypothesis H_nex : ~ (exists k, k <= n+1 /\ f k = f (n+2)).
Goal (f (n+2) <= n).
I tried to use not_ex_all_not from Coq.Logic.Classical_Pred_Type.
Check not_ex_all_not.
not_ex_all_not
: forall (U : Type) (P : U -> Prop),
~ (exists n : U, P n) -> forall n : U, ~ P n
apply not_ex_all_not in H_nex.
Error: Unable to find an instance for the variable n.
I don't understand what this error means, so as a random guess I tried this:
apply not_ex_all_not with (n := n) in H_nex.
It succeeds but H_nex is complete nonsense now:
H_nex : ~ (n <= n+1 /\ f n = f (n + 2))
On the other hand it is easy to solve my goal if H_nex is expressed as forall:
Hypothesis H_nex : forall k, k <= n+1 -> f k <> f (n+2).
specialize (H_nex i).
specialize (Hf (n+2)).
omega.
I found similar question but failed to apply it to my case.
If you want to use the not_ex_all_not lemma, what you want to proof needs to look like the lemma. E.g. you can proof the following first:
Lemma lma {n:nat} {f:nat->nat} : ~ (exists k, k <= n /\ f k = f (n+1)) ->
forall k, ~(k <= n /\ f k = f (n+1)).
intro H.
apply not_ex_all_not.
trivial.
Qed.
and then proof the rest:
Theorem thm (n:nat) (f:nat->nat) : ~ (exists k, k <= n /\ f k = f (n+1)) ->
forall k, k <= n -> f k <> f (n+1).
intro P.
specialize (lma P). intro Q.
intro k.
specialize (Q k).
tauto.
Qed.
I'm not quite sure what your problem is.
Here is how to show trivially that your implication holds.
Section S.
Variable n : nat.
Variable f : nat -> nat.
Hypothesis H : ~ (exists k, k <= n /\ f k = f (n+1)).
Goal forall k, k <= n -> f k <> f (n+1).
Proof.
intros k H1 H2.
apply H.
exists k.
split; assumption.
Qed.
End S.
Also your goal is provable by apply Hf., so I'm not sure but you seem to have some confusion...