Prove equality on Sigma-types - coq

I have defined a Sygma-Type that looks like:
{ R : nat -> nat -> bool | Reflexive R }
I have two elements r1 r2 : { R : nat -> nat -> bool | Reflexive R } and I am to prove r1 = r2. How can I do that?

If you want to show such an equality, you need to (1) show that the underlying functions are equal (i.e., the R component of your sigma type), and (2) show that the corresponding proofs are equal. There are two problems, however.
The first one is that equality of functions is too weak in Coq. According to common mathematical practice, we expect two functions to be equal if they yield equal results for any inputs. This principle is known as functional extensionality:
Axiom functional_extensionality :
forall A (B : A -> Type)
(f g : forall a, B a),
(forall x, f x = g x) ->
f = g.
As natural as it sounds, however, this principle is not provable in Coq's logic! Roughly speaking, the only way two functions can be equal is if they can be converted to a syntactically equal terms according to the computation rules of the logic. For instance, we can show that fun n : nat => 0 + n and fun n : nat => n are equal because + is defined in Coq by pattern-matching on the first argument, and the first argument on the first term is 0.
Goal (fun n : nat => 0 + n) = (fun n : nat => n). reflexivity. Qed.
We could expect to show that fun n => n + 0 and fun n => n are equal by similar means. However, Coq does not accept this, because + cannot be simplified when the first argument is a variable.
The other problem is that the notion of equality on proofs is not very interesting as well. The only way one can show that two proofs are equal is, again, syntactic equality. Intuitively, however, one would like to argue by proof irrelevance, a principle that states that proofs of the same thing are always equal:
Axiom proof_irrelevance :
forall (P : Prop) (p q : P), p = q.
but, again, this principle is not provable in the logic. Fortunately, Coq's logic was designed to allow one to add these principles as axioms in a sound way. One then gets the following proof:
Axiom functional_extensionality :
forall A (B : A -> Type)
(f g : forall a, B a),
(forall a, f a = g a) ->
f = g.
Axiom proof_irrelevance :
forall (P : Prop) (p q : P), p = q.
Lemma l (r1 r2 : { R : nat -> nat -> bool |
forall n, R n n = true }) :
(forall n1 n2, proj1_sig r1 n1 n2 = proj1_sig r2 n1 n2) ->
r1 = r2.
Proof.
destruct r1 as [r1 H1], r2 as [r2 H2].
simpl.
intros H.
assert (H' : r1 = r2).
{ apply functional_extensionality.
intros n1.
apply functional_extensionality.
intros n2.
apply H. }
subst r2.
rename r1 into r.
f_equal.
apply proof_irrelevance.
Qed.
Even though axioms can be useful, one might like to avoid them. In this case, it is actually possible to prove this lemma just with functional extensionality, but you do need at least that. If you want to avoid using axioms, and r1 and r2 are not equal up to computation, you'll have to use a difference equivalence relation on your type, and do your formalization using that relation instead, e.g.
Definition rel_equiv (r1 r2 : { R : nat -> nat -> bool | forall n, R n n = true }) : Prop :=
forall n1 n2, proj1_sig r1 n1 n2 = proj2_sig r2 n1 n2.
The standard library has good support for rewriting with equivalence relations; cf. for instance this.

Related

Destruct hypothesis: general case

That's pretty clear what destruct H does if H contains conjunction or disjunction. But I can't figure out what it does in general case. It does something bizarre, especially if H: a -> b.
Some examples:
Lemma demo : forall (x y: nat), x=4 -> x=4.
Proof.
intros. destruct H.
The hypothesis is just destroyed:
1 subgoal
x, y : nat
______________________________________(1/1)
x = x
Another one:
Lemma demo : forall (x y: nat), (x = 4 -> x=4) -> True.
Proof.
intros. destruct H.
Now I have two branches:
1 subgoal
x, y : nat
______________________________________(1/1)
x = 4
1 subgoal
x, y : nat
______________________________________(1/1)
True
Third example. It's not provable but it still doesn't make sense to me:
Lemma demo : forall (x y: nat), (x = 4 -> x = 4) -> x = 4.
Proof.
intros. destruct H.
Now I have to prove x = x in the second branch!
2 subgoals
x, y : nat
______________________________________(1/2)
x = 4
______________________________________(2/2)
x = x
So, I clearly don't understand what destruct H does.
The cases you are referring to fall in two categories. If H : A and A is inductively or coinductively defined (e.g., conjunction and disjunction), then destruct H generates one subgoal for each constructor in that type, with additional hypotheses determined by the arguments of that constructor. On the other hand, if H : A -> B, then destruct H generates one subgoal where you have to prove A, and then continues recursively as if H : B. This is roughly equivalent to the following calls:
assert (H' : A); [ |specialize (H H'); destruct H].
The missing piece of the puzzle is that equality itself is defined as an inductive type:
Inductive eq (A : Type) (a : A) : A -> Prop :=
| eq_refl : eq A a a
When you destruct something of type x = 4, Coq generates one case for each constructor of that type. But there is only one constructor in that type: eq_refl. When considering that case, Coq also automatically replaces occurrences of the RHS of destructed equality by the LHS (since both sides are equal for that constructor). In your first and third examples, this leads to replacing 4 in the goal with x.
Most of the time, you do not want to destruct an equality hypothesis, since this replacement behavior is not very useful. It is usually better to use the rewrite tactic, since it allows you to rewrite from rightto-left or left-to-right.

Quantification under sumbool

I have found it quite difficult to work with goals of the form
{forall _, _} + { not (forall _, _) }
Consider this for a minimal example:
Inductive X := a | b.
Proposition X_q_dec :
forall P : X -> Prop,
(forall x, { P x } + { not (P x) }) ->
{ forall x, P x } + { not (forall x, P x) }.
Proof.
intros.
pose proof H a as Ha.
pose proof H b as Hb.
destruct Ha, Hb.
left; destruct x; assumption.
all: right; intro C; auto.
Qed.
Since X has a finite number of elements, there is a finite number of decidable predicates on it. Thus in this proof I just look over all of them and prove the correct side of sumbool for each one.
However, in the more general case, I cannot move the goal at all:
Proposition nat_q_dec :
forall P : nat -> Prop,
(forall n, { P n } + { not (P n) }) ->
{ forall n, P n } + { not (forall n : nat, P n) }.
My gut feeling is that this should be undecidable: there is no algorithm with a finite upper boundary on operations required to determine whether or not P holds on all n : nat. The same, it seems, should apply to any other type with an infinite number of elements (i.e. not just nat). Unfortunately, my theoretical background falls short to provide a rigorous argument about this.
Is my feeling correct?
If so, why exactly?
If not, how should I go about proving such a goal in Coq?
Yes, you are right, and the argument is pretty much what you laid out. Suppose that P t n means that "the lambda term t can be normalized using at most n reduction steps". Then ~ P t n should be decidable for every n, but it is not the case that we can decide forall n, ~ P t n, since this would imply solving the halting problem.

Proof leaking in Coq extraction?

In order to understand how general recursive Function definitions works, and how they comply with Coq's structural recursion constraint, I tried to reimplement it on the Peano natural numbers. I want to define recursive nat -> nat functions that can use any previous values, not just the predecessor. Here is what I did :
Definition nat_strong_induction_set
(* erased at extraction, type specification *)
(P : nat -> Set)
(* The strong induction step. To build the P n it can, but does not have to,
recursively query the construction of any previous P k's. *)
(ind_step : forall n : nat, (forall k : nat, (lt k n -> P k)) -> P n)
(n : nat)
: P n.
Proof.
(* Force the hypothesis of ind_step as a standard induction hypothesis *)
assert (forall m k : nat, lt k m -> P k) as partial_build.
{ induction m.
- intros k H0. destruct k; inversion H0.
- intros k H0. apply ind_step. intros k0 H1. apply IHm. apply (lt_transitive k0 k).
assumption. apply le_lt_equiv. assumption. }
apply (partial_build (S n) n). apply succ_lt.
Defined.
I used some custom lemmas on nats that I didn't paste here. It works, I managed to define the euclidean division div a b with it, which recursively uses div (a-b) b. The extraction is almost what I expected :
let nat_strong_induction_set ind_step n =
let m = S n in
let rec f n0 k =
match n0 with
| O -> assert false (* absurd case *)
| S n1 -> ind_step k (fun k0 _ -> f n1 k0)
in f m n
Except for the n0 parameter. We see that the only effect of this parameter is to stop the recursion at the S n-nth step. The extraction also mentions that this assert false should not happen. So why is it extracted ? This seems better
let nat_strong_induction_set ind_step n =
let rec f k = ind_step k (fun k0 _ -> f k0)
in f n
It looks like a glitch of Coq's structural recursion constraint, to ensure the termination of all recursions. The Coq definition of nat_strong_induction_set writes lt k n, so Coq knows only previous P k's will be queried. This makes a decreasing chain in the nats, which is forced to terminate in less than S n steps. This allows a structural recursive definition on an additional fuel parameter n0 starting at S n, it won't affect the result. So if it is only a part of the termination proof, why is it not erased by the extraction ?
Your match is not erased because your definition mixes two things: the termination argument, where the match is needed, and the computationally relevant recursive call, where it isn't.
To force erasure, you need to convince Coq that the match is computationally irrelevant. You can do so by making the termination argument -- that is, the induction on m -- produce the proof of a proposition instead of a function of type forall m k, lt k m -> P k. Luckily, the standard library provides an easy way of doing so, with the Fix combinator:
Require Import Coq.Arith.Wf_nat.
Definition nat_strong_induction_set
(P : nat -> Set)
(ind_step : forall n : nat, (forall k : nat, (lt k n -> P k)) -> P n)
(n : nat)
: P n :=
Fix lt_wf P ind_step n.
Here, lt_wf is a proof that lt is well-founded. When you extract this function, you get
let rec nat_strong_induction_set ind_step n =
ind_step n (fun y _ -> nat_strong_induction_set ind_step y)
which is exactly what you wanted.
(As an aside, note that you don't need well-founded recursion to define division -- check for instance how it is defined in the Mathematical Components library.)

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.

How does 'elim' in Coq work on existential quantifier?

I'm confused by Coq on its way dealing with existential quantification.
I have a predicate P and an assumption H
P : nat -> Prop
H : exists n, P n
while the current goal is (whatever)
(Some goal)
If I want to instantiate n in H, I will do
elim H.
However after the elimination, the current goal becomes
forall n, P n -> (Some goal)
It looks like Coq converts an existential quantifier into a universal one. I know that (forall a, P a -> Q a) -> ((exists a, P a) -> Q a) out of my limited knowledge on first-order logic. But the reverse proposition seems to be incorrect. If the 'forall' one and 'exists' one are not equivalent, why Coq would do such conversion?
Does 'elim' in Coq replace the goal with a harder to prove one? Or could anyone please show why ((exists a, P a) -> Q a) -> (forall a, P a -> Q a) holds in first-order logic?
Maybe the missing key is that the goal:
forall n, P n -> (Some goal)
is to be read as:
forall n, (P n -> (Some goal))
and not as:
(forall n, P n) -> (Some goal)
That is, the goal you are given just gives you an arbitrary n and a proof P n, which is indeed the proper way to eliminate an existential (you don't get to know the value of the witness since it could be any value that makes P true, you just get to know that there is a n and that P n holds).
On the contrary, the latter would provide you with a function that can build P n for any n you pass it, which is indeed a stronger statement than the one you have.
I realize this question is old but I would like to add the following important clarification:
In Coq, (and more generally, in intuitionistic logic) the existential quantifier is defined (see here) as follows
(exists x, (P x)) := forall (P0 : Prop), ((forall x, (P x -> P0)) -> P0)
Intuitively this can be read as
(exists x, P x) is the smallest proposition which holds whenever P x0 holds for some x0
In fact one can easily prove the following two theorems in Coq:
forall x0, (P x0 -> (exists x, P x)) (* the introduction rule -- proved from ex_intro *)
and (provided A : Prop)
(exists x : A, P x) -> {x : A | P x} (* the elimination rule -- proved from ex_ind *)
So a Coq goal of the form
H1...Hn, w : (exists x, P x) |- G
is transformed (using elim) to a Coq goal of the form
H1...Hn, w : (exists x, P x) |- forall x0, (P x0 -> G)
because whenever h : forall x0, (P x0 -> G), then G is precisely justified by the proof term
(ex_ind A P G h w) : G
which works whenever G : Prop.
Note: the elimination rule above is only valid whenever A : Prop, and cannot be proved whenever A : Type. In Coq, this mean that we do not have the ex_rect eliminator.
From my understanding (see here for more details), this is a design choice to preserve good program extraction properties.