Is CoInductive "extensionality" sound in Coq? Is it generalizable? - coq

My understanding is that the typical notion of equality is too weak to prove many intuitive equalities on possibly infinite coinductive terms. Therefore it is necessary to introduce a coinductive equality for the specific coinductive type in question.
For instance, I have the following coinductive definitions concerning infinite relation transition sequences:
Section Paths.
Context {state : Type}.
Variable R : relation state.
CoInductive path (s: state) : Type :=
| step : forall s', R s s' -> path s' -> path s.
CoInductive path_eq : forall {s}, path s -> path s -> Prop :=
| path_eq_intro : forall x y r p p',
path_eq p p' ->
path_eq (step x y r p) (step x y r p').
It would be quite desirable then to extend our core equality accordingly:
Axiom path_extensionality : forall s (p q: path s),
path_eq p q -> p = q.
While this axiom makes sense intuitively, are such coinductive extensionality principles known to be sound in general?
Also, I'm concerned that I would have to add a new axiom for each coinductive type I use. Is there a generic way to introduce an extensionality principle for arbitrary coinductive types?

While this axiom makes sense intuitively, are such coinductive extensionality principles known to be sound in general?
Yes. There's an isomorphism between coinductive types and function types. A coinductive type modulo coinductive extensionality is isomorphic to the type of functions from it's index type to its data type modulo function extensionality. So, for example, Stream A modulo EqSt (from Coq.Lists.Streams) is isomorphic to nat -> A modulo function extensionality. Your datatype modulo path_extensionality is roughly isomorphic to { st : nat -> state | forall n, R (st n) (st (S n)) } modulo function extensionality. The trick I'm pulling with your datatype here is to transform it into one that is parameterized instead of indexed; your datatype is morally parameterized over Stream state.
There's unfortunately no way to introduce all the extensionality axioms at once, unless you want to give up coinductives and switch to functions (in which case you can just use the standard library axiom Coq.Logic.FunctionalExtensionality.functional_extensionality_dep). However, you could use a weaker axiom combined with function extensionality: the weaker axiom for a given coinductive type would say that if you go from the coinductive to the corresponding function and then back, you get back what you started with. Another way of phrasing this is that you can axiomatize only one direction of the isomorphism, rather than both directions. This is weaker in the sense that each coinductive extensionality axiom implies function extensionality for the corresponding function type, but this axiom does not.
In case you want some code, here's some code showing the formal isomorphism for standard library streams and for your type (works in Coq 8.13):
Require Import Coq.Lists.Streams.
Require Import Coq.Setoids.Setoid.
Module StreamsExt.
Section __.
Context (A : Type).
Definition t := Stream A.
Definition index := nat.
Definition data := A.
(** inclusion, injection, section, monomorphism *)
(** From a function [nat -> A], we can build a [Stream A] *)
CoFixpoint sect (x : index -> data) : t
:= Streams.Cons (x 0) (sect (fun n => x (S n))).
(* surjection, retraction, epimorphism *)
(** From a [Stream A], we can get the nth element, building a function [nat -> A] *)
Fixpoint retr (x : t) (i : index) : data
:= match i with
| O => Streams.hd x
| S i => retr (Streams.tl x) i
end.
(** Two streams are coextensionally equal, then the corresponding functions are extensionally equal *)
Lemma iff_EqSt_pointwise_eq (s1 s2 : t) : Streams.EqSt s1 s2 <-> forall i, retr s1 i = retr s2 i.
Proof using Type.
split.
{ intros H i; revert s1 s2 H; induction i as [|i IHi]; cbn; intros s1 s2 [H1 H2]; auto. }
{ revert s1 s2; cofix CIH; intros s1 s2 H.
constructor.
{ specialize (H 0); cbn in H. assumption. }
{ specialize (fun i => H (S i)); cbn in H. auto. } }
Qed.
Lemma ext_from_funext
(funext : forall f g : index -> data, (forall x, f x = g x) -> f = g)
(axiom : forall x, sect (retr x) = x)
: forall (s1 s2 : t), EqSt s1 s2 -> s1 = s2.
Proof using Type.
intros s1 s2; rewrite iff_EqSt_pointwise_eq; intro H.
rewrite <- (axiom s1), <- (axiom s2); apply f_equal, funext, H.
Qed.
End __.
End StreamsExt.
Section Paths.
Context {state : Type}.
Variable R : relation state.
CoInductive path (s: state) : Type :=
| step : forall s', R s s' -> path s' -> path s.
CoInductive path_eq : forall {s}, path s -> path s -> Prop :=
| path_eq_intro : forall x y r p p',
path_eq p p' ->
path_eq (step x y r p) (step x y r p').
Definition next_state {s} (p : path s) : state
:= match p with
| #step _ s' _ _ => s'
end.
Definition unstep_rel {s} (p : path s) : R s (next_state p)
:= match p with
| #step _ _ r _ => r
end.
Definition unstep {s} (p : path s) : path (next_state p)
:= match p with
| #step _ _ _ p => p
end.
(* slightly nicer version of path_eq *)
Import EqNotations.
CoInductive path_eq' {s1 s2} (p1 : path s1) (p2 : path s2) (H : s1 = s2) : Prop :=
| path_eq'_intro :
forall (p : next_state p1 = next_state p2),
(rew [fun s => R s _] H in rew p in unstep_rel p1) = unstep_rel p2 ->
path_eq' (unstep p1) (unstep p2) p ->
path_eq' p1 p2 H.
CoFixpoint path_eq'_of_path_eq {s} {p1 p2 : path s}
: path_eq p1 p2 -> path_eq' p1 p2 eq_refl.
Proof using Type.
intro H; unshelve econstructor; destruct H.
{ cbn. reflexivity. }
{ cbn. reflexivity. }
{ cbn. apply path_eq'_of_path_eq, H. }
Qed.
Lemma next_state_eq_of_path_eq' {s1 s2} {p1 : path s1} {p2 : path s2} {H : s1 = s2}
: path_eq' p1 p2 H -> next_state p1 = next_state p2.
Proof using Type.
intro H'; destruct H'; assumption.
Defined.
Lemma rel_eq_of_path_eq' {s1 s2} {p1 : path s1} {p2 : path s2} {H : s1 = s2}
: forall p : path_eq' p1 p2 H, (rew [fun s => R s _] H in rew next_state_eq_of_path_eq' p in unstep_rel p1) = unstep_rel p2.
Proof using Type.
intro H'; destruct H'; cbn in *; assumption.
Defined.
CoFixpoint path_eq_of_path_eq' {s} {p1 p2 : path s}
: path_eq' p1 p2 eq_refl -> path_eq p1 p2.
Proof using Type.
intro H'; destruct p1, p2, H'; cbn in *; subst; cbn in *.
constructor; auto.
Qed.
Lemma iff_path_eq_path_eq'_eq_refl {s} (p1 p2 : path s)
: path_eq p1 p2 <-> path_eq' p1 p2 eq_refl.
Proof using Type.
split; first [ apply path_eq'_of_path_eq | apply path_eq_of_path_eq' ].
Qed.
Lemma iff_path_eq_path_eq' {s1 s2} (p1 : path s1) (p2 : path s2) (H : s1 = s2)
: path_eq (rew H in p1) p2 <-> path_eq' p1 p2 H.
Proof using Type.
subst; apply iff_path_eq_path_eq'_eq_refl.
Qed.
End Paths.
Module PathsExt.
Section __.
Context {state : Type}.
Variable R : relation state.
Definition t := { s : state & path R s }.
Definition fun_type := { st : nat -> state | forall n, R (st n) (st (S n)) }.
Definition mk_fun_type (st : nat -> state) (H : forall n, R (st n) (st (S n))) : fun_type
:= exist _ st H.
(** inclusion, injection, section, monomorphism *)
CoFixpoint sect' (x : fun_type) : path R (proj1_sig x 0)
:= step
R _ _
(proj2_sig x 0)
(sect' (mk_fun_type (fun i => proj1_sig x (S i)) (fun i => proj2_sig x (S i)))).
Definition sect (x : fun_type) : t := existT _ _ (sect' x).
(* surjection, retraction, epimorphism *)
Fixpoint retr_index (x : t) (i : nat) : state
:= match i with
| O => projT1 x
| S i => retr_index (existT _ _ (unstep _ (projT2 x))) i
end.
Fixpoint retr' (x : t) (i : nat) : R (retr_index x i) (retr_index x (S i))
:= match i with
| O => unstep_rel _ (projT2 x)
| S i => retr' (existT _ _ (unstep _ (projT2 x))) i
end.
Definition retr (x : t) : fun_type := mk_fun_type (retr_index x) (retr' x).
Import EqNotations.
(** Two paths are coextensionally equal, then the corresponding functions are extensionally equal *)
Definition t_eq (s1 s2 : t) : Prop
:= { p : projT1 s1 = projT1 s2 | path_eq' _ (projT2 s1) (projT2 s2) p }.
Definition fun_type_eq (s1 s2 : fun_type) : Prop
:= exists p : (forall i, proj1_sig s1 i = proj1_sig s2 i),
forall i, (rew [fun s => R s _] (p i) in rew (p (S i)) in proj2_sig s1 i) = proj2_sig s2 i.
Lemma iff_path_eq_pointwise_eq (s1 s2 : t) : t_eq s1 s2 <-> fun_type_eq (retr s1) (retr s2).
Proof using Type.
split; cbv [t_eq fun_type_eq].
{ intro H; unshelve eexists; intro i; revert s1 s2 H; induction i as [|i IHi]; cbn; intros [s1 s1'] [s2 s2'] [H1 H2];
cbn in *.
{ assumption. }
{ apply IHi; cbn.
unshelve eexists; destruct H2; eassumption. }
{ cbn. destruct H2; assumption. }
{ cbn in *. apply IHi. } }
{ intros [H1 H2].
exists (H1 0).
revert s1 s2 H1 H2; cofix CIH; intros.
unshelve econstructor.
{ exact (H1 1). }
{ exact (H2 O). }
{ apply (CIH _ _ (fun i => H1 (S i)) (fun i => H2 (S i))). } }
Defined.
Lemma ext_from_funext'
(funext_fun_type : forall f g, fun_type_eq f g -> f = g)
(axiom : forall x, sect (retr x) = x)
(funext_fun_type_pr : forall f g p q, f_equal (fun a => proj1_sig a 0) (funext_fun_type f g (ex_intro _ p q)) = p 0)
(axiom_pr1 : forall x, f_equal (#projT1 _ _) (axiom x) = eq_refl)
: forall (p1 p2 : t) (H : t_eq p1 p2), { pf : p1 = p2 | f_equal (#projT1 _ _) pf = proj1_sig H }.
Proof using Type.
intros p1 p2.
intro H; unshelve eexists.
{ apply iff_path_eq_pointwise_eq, funext_fun_type, (f_equal sect) in H.
etransitivity; [ | apply axiom ]; etransitivity; [ symmetry; apply axiom | ].
exact H. }
{ cbv zeta beta.
rewrite !eq_trans_map_distr, <- !eq_sym_map_distr, !axiom_pr1; cbn [eq_sym].
rewrite !eq_trans_refl_l, !eq_trans_refl_r, !f_equal_compose.
cbn [projT1 sect].
cbv [iff_path_eq_pointwise_eq]; cbn.
rewrite funext_fun_type_pr.
cbn.
destruct p1, p2, H; cbn; reflexivity. }
Qed.
Lemma f_equal_projT1_eq_sigT {A P u v p q}
: f_equal (#projT1 _ _) (#eq_sigT A P u v p q) = p.
Proof using Type. destruct u, v; cbn in *; subst; cbn; reflexivity. Qed.
Lemma ext_from_funext''
(funext_nat : forall f g : nat -> state, (forall x, f x = g x) -> f = g)
(funext_rel : forall F (f g : forall n : nat, R (F n) (F (S n))), (forall x, f x = g x) -> f = g)
(axiom : forall x, sect (retr x) = x)
(funext_nat_f_equal : forall f g p x, f_equal (fun f => f x) (funext_nat f g p) = p x)
(axiom_pr1 : forall x, f_equal (#projT1 _ _) (axiom x) = eq_refl)
: forall {s} (p1 p2 : path R s), path_eq R p1 p2 -> p1 = p2.
Proof using Type.
intros s p1 p2 H.
apply iff_path_eq_path_eq'_eq_refl in H.
unshelve epose proof (ext_from_funext' _ _ _ _ (existT _ _ _) (existT _ _ _) (exist _ eq_refl H)) as H'; try eassumption; revgoals.
{ destruct H' as [H0 H']; cbn in *.
induction H0 using (#eq_sigT_ind _ _ _ _); cbn [projT1 projT2] in *.
rewrite f_equal_projT1_eq_sigT in H'; subst; cbn in *.
reflexivity. }
all: revgoals.
{ cbv [fun_type_eq].
intros [f1 f2] [g1 g2] [H1 H2]; cbn in *.
apply eq_sig_uncurried; cbn.
exists (funext_nat _ _ H1).
apply funext_rel; intro n.
etransitivity; [ | apply H2 ].
transitivity (rew [fun s => R s _] f_equal (fun f => f _) (funext_nat _ _ H1) in rew [R _] f_equal (fun f => f _) (funext_nat _ _ H1) in f2 n).
{ generalize (funext_nat _ _ H1); intro; subst; reflexivity. }
{ rewrite <- (funext_nat_f_equal _ _ H1 n), <- (funext_nat_f_equal _ _ H1 (S n)); reflexivity. } }
{ intros; cbn.
destruct f, g; cbn in *.
move funext_nat at bottom.
generalize (funext_nat_f_equal _ _ p).
generalize (funext_nat _ _ p).
clear funext_nat funext_nat_f_equal.
intro; subst; cbn in *.
intro funext_nat_f_equal.
destruct funext_rel; cbn; apply funext_nat_f_equal. }
Qed.
(* quoting myself at https://github.com/HoTT/HoTT/issues/757#issue-76140493 *)
Definition good_funext_of_funext {A B}
(funext : forall f g : A -> B, (forall x, f x = g x) -> f = g)
(funext_dep : forall (f : A -> B) (F G : forall x : A, {x0 : B & f x = x0}),
(forall x, F x = G x) -> F = G)
: { funext : forall f g : A -> B, (forall x, f x = g x) -> f = g
| (forall f g p, funext f g (fun x => f_equal (fun f => f x) p) = p)
/\ (forall f g p x, f_equal (fun f => f x) (funext f g p) = p x) }.
Proof using Type.
exists (fun f g p => eq_trans (eq_sym (funext _ _ (fun _ => eq_refl))) (funext f g p)).
split.
{ intros; subst; cbn; apply eq_trans_sym_inv_l. }
{ intros f g p.
pose (fun x => existT _ (g x) (p x)) as p'.
change p with (fun x => projT2 (p' x)).
change g with (fun x => projT1 (p' x)).
clearbody p'; clear p g.
assert (H'' : forall x, existT _ (f x) eq_refl = p' x).
{ intro x.
destruct (p' x) as [? []]; reflexivity. }
intro x.
apply funext_dep in H''; subst p'; cbn.
rewrite eq_trans_sym_inv_l; reflexivity. }
Qed.
Lemma ext_from_funext_specific
(funext_nat : forall f g : nat -> state, (forall x, f x = g x) -> f = g)
(funext_nat_sig : forall (f : nat -> state) (F G : forall x : nat, {x0 : state & f x = x0}),
(forall x, F x = G x) -> F = G)
(funext_rel : forall F (f g : forall n : nat, R (F n) (F (S n))), (forall x, f x = g x) -> f = g)
(axiom : forall x, projT2 (sect (retr x)) = projT2 x)
: forall {s} (p1 p2 : path R s), path_eq R p1 p2 -> p1 = p2.
Proof using Type.
unshelve eapply ext_from_funext''.
{ unshelve eapply good_funext_of_funext.
{ exact funext_nat. }
{ exact funext_nat_sig. } }
{ intro x.
unshelve eapply eq_sigT.
{ reflexivity. }
{ apply axiom. } }
{ apply funext_rel. }
{ cbn; intros f g p n.
set (fs := good_funext_of_funext _ _).
destruct fs as [fs [H1 H2]].
apply H2. }
{ cbv beta.
intro; apply f_equal_projT1_eq_sigT. }
Qed.
Lemma ext_from_funext_dep
(funext_dep : forall A B (f g : forall a : A, B a), (forall x, f x = g x) -> f = g)
(axiom : forall x, projT2 (sect (retr x)) = projT2 x)
: forall {s} (p1 p2 : path R s), path_eq R p1 p2 -> p1 = p2.
Proof using Type.
apply ext_from_funext_specific; try first [ intros *; apply funext_dep | apply axiom ].
Qed.
Lemma f_equal_id {A x y p} : #f_equal A A id x y p = p.
Proof using Type. destruct p; reflexivity. Qed.
(** A more conceptually separated version of this is at
https://github.com/HoTT/HoTT/blob/master/theories/Metatheory/FunextVarieties.v
*)
Lemma ext_from_funext
(funext : forall A B (f g : A -> B), (forall x, f x = g x) -> f = g)
(axiom : forall x, projT2 (sect (retr x)) = projT2 x)
: forall {s} (p1 p2 : path R s), path_eq R p1 p2 -> p1 = p2.
Proof using Type.
apply ext_from_funext_dep; [ | apply axiom ].
intros A B f g H.
pose (existT (fun F : A -> { x : A & { y : B x & f x = y } } => (fun x => projT1 (F x)) = id)
(fun x : A => existT _ x (existT _ (f x) eq_refl))
eq_refl) as F.
pose (existT (fun G : A -> { x : A & { y : B x & f x = y } } => (fun x => projT1 (G x)) = id)
(fun x : A => existT _ x (existT _ (g x) (H x)))
eq_refl) as G.
cut (F = G).
{ intro H'.
change ((rew [fun F => forall x, B (F x)] projT2 F in (fun x => projT1 (projT2 (projT1 F x)))) =
(rew [fun F => forall x, B (F x)] projT2 G in (fun x => projT1 (projT2 (projT1 G x))))).
clearbody F G; subst G; reflexivity. }
{ clearbody F G.
pose (fun F : A -> {x : A & {y : B x & f x = y}} => fun x => projT1 (F x)) as postcomp_projT1.
pose (fun F : A -> A
=> (fun x : A => existT (fun x : A => {y : B x & f x = y}) (F x) (existT _ _ eq_refl))) as inv.
assert (H' : forall F, inv (postcomp_projT1 F) = F).
{ clear -funext.
intro F.
apply funext; intro x.
subst inv postcomp_projT1; cbn.
destruct (F x) as [? [? []]]; cbn; reflexivity. }
simple refine (let H'' : forall F, postcomp_projT1 (inv F) = F := _ in _).
{ reflexivity. }
cut (forall Fid (F : {G : A -> {x : A & {y : B x & f x = y}} & postcomp_projT1 G = Fid}),
F = existT _ (fun x => existT _ _ (existT _ _ eq_refl)) eq_refl).
{ intro contr; etransitivity; [|symmetry]; apply contr. }
{ clear F G.
intros Fid F.
refine (eq_sym (#eq_trans _ _ (existT _ _ (H'' Fid)) _ _ (eq_sym _))).
{ apply eq_sigT_uncurried; subst inv postcomp_projT1; cbn.
exists eq_refl; cbv.
reflexivity. }
{ simple refine (let H''' : forall F, inv (postcomp_projT1 F) = F := fun F => eq_trans (eq_trans (f_equal inv (f_equal postcomp_projT1 (eq_sym (H' F)))) (f_equal inv (H'' _))) (H' F) in
_).
clearbody H''.
apply eq_sigT_uncurried; cbn.
exists (eq_trans (eq_sym (H''' _)) (f_equal inv (projT2 F))).
destruct F as [F ?]; subst.
subst H'''; cbn.
clear -funext H' H''.
clearbody inv postcomp_projT1.
clear -funext H' H''.
match goal with
| [ |- rew ?p in eq_refl = ?H ] => cut (f_equal postcomp_projT1 (eq_sym p) = H); [ generalize p | ]
end.
{ clear.
generalize (H'' (postcomp_projT1 F)).
generalize (inv (postcomp_projT1 F)).
intros; subst; cbn; reflexivity. }
rewrite !eq_sym_involutive, !eq_trans_map_distr.
cut (eq_trans
(f_equal postcomp_projT1 (f_equal inv (H'' (postcomp_projT1 F)))) (f_equal postcomp_projT1 (H' F)) =
eq_trans (f_equal postcomp_projT1 (f_equal inv (f_equal postcomp_projT1 (H' F)))) (H'' (postcomp_projT1 F))).
{ rewrite <- !eq_sym_map_distr.
intro H.
rewrite <- eq_trans_assoc, H, !eq_trans_assoc, eq_trans_sym_inv_l, eq_trans_refl_l.
reflexivity. }
generalize (f_equal postcomp_projT1 (H' F)).
generalize (postcomp_projT1 F).
cut (forall a b (e : postcomp_projT1 (inv a) = b),
eq_trans (f_equal postcomp_projT1 (f_equal inv (H'' a))) e = eq_trans (f_equal postcomp_projT1 (f_equal inv e)) (H'' b)).
{ clear; intros H ??; apply H. }
intros; subst b; cbn.
rewrite eq_trans_refl_l.
rewrite f_equal_compose.
set (F' := fun a => postcomp_projT1 (inv a)).
change (forall F, F' F = F) in H''.
change (f_equal F' (H'' a) = H'' (F' a)).
assert (Hid : F' = id) by (apply funext, H'').
clearbody F'; subst F'.
rewrite f_equal_id; reflexivity. } } }
Qed.
End __.
End PathsExt.

Related

"Cannot instantiate metavariable P of type ..." when destructing in Coq proof mode

I have a problem in proving trivial proposition.
First, We define a composition of function with general domain and codomain:
Definition fun_comp {X Y Z W}
(f : X -> Y) (g : Z -> W) (H : Y = Z) : X -> W.
destruct H. refine (fun x => g (f x)). Defined.
We will now try to prove a trivial lemma:
Lemma compose_trivial {X Y Z} (f : X -> Y) (g : Y -> Z) (H : Y = Y)
: forall x, fun_comp f g H x = g (f x).
Proof.
intros x. revert f g. destruct H.
But destruct H. fails with an error message:
Cannot instantiate metavariable P of type
"forall a : Type, Y = a -> Prop" with abstraction
"fun (Y : Type) (H : Y = Y) =>
forall (f : X -> Y) (g : Y -> Z), fun_comp f g H x = g (f x)"
of incompatible type
"forall Y : Type, Y = Y -> Prop".
If it is able to generalize Y in the right hand side of H independently, the destruct tactic would work, but it would contradict to the right hand side of the goal g (f x).
Is it possible to prove trivial_compose? If possible, how can I?
This is not trivial at all. This has to do with uniqueness of identity proof that is not provable in Coq. You need an extra axiom.
For example,
Require Import ProofIrrelevance.
Lemma compose_trivial {X Y Z} (f : X -> Y) (g : Y -> Z) (H : Y = Y)
: forall x, fun_comp f g H x = g (f x).
Proof.
intros x.
now rewrite <- (proof_irrelevance _ (eq_refl Y) H).
Qed.

How to prove all proofs of le equal?

I'm basically trying to prove
Theorem le_unique {x y : nat} (p q : x <= y) : p = q.
without assuming any axioms (e.g. proof irrelevance). In particular, I've tried to get through le_unique by induction and inversion, but it never seems to get far
Theorem le_unique (x y : nat) (p q : x <= y) : p = q.
Proof.
revert p q.
induction x as [ | x rec_x]. (* induction on y similarly fruitless; induction on p, q fails *)
- destruct p as [ | y p].
+ inversion q as [ | ]. (* destruct q fails and inversion q makes no progress *)
admit.
+ admit.
- admit.
Admitted.
In the standard library, this lemma can be found as Peano_dec.le_unique in the module Coq.Arith.Peano_dec.
As for a relatively simple direct proof, I like to go by induction on p itself.
After proving by hand a few induction principles that Coq doesn't automatically generate, and remembering that proofs of equality on nat are unique, the proof is a relatively straightforward induction on p followed by cases on q, giving four cases two of which are absurd.
Below is a complete Coq file proving le_unique.
Import EqNotations.
Require Eqdep_dec PeanoNat.
Lemma nat_uip {x y : nat} (p q : x = y) : p = q.
apply Eqdep_dec.UIP_dec.
exact PeanoNat.Nat.eq_dec.
Qed.
(* Generalize le_ind to prove things about the proof *)
Lemma le_ind_dependent :
forall (n : nat) (P : forall m : nat, n <= m -> Prop),
P n (le_n n) ->
(forall (m : nat) (p : n <= m), P m p -> P (S m) (le_S n m p)) ->
forall (m : nat) (p : n <= m), P m p.
exact (fun n P Hn HS => fix ind m p : P m p := match p with
| le_n _ => Hn | le_S _ m p => HS m p (ind m p) end).
Qed.
(*
Here we give an proof-by-cases principle for <= which keeps both the left
and right hand sides fixed.
*)
Lemma le_case_remember (x y : nat) (P : x <= y -> Prop)
(IHn : forall (e : y = x), P (rew <- e in le_n x))
(IHS : forall y' (q' : x <= y') (e : y = S y'), P (rew <- e in le_S x y' q'))
: forall (p : x <= y), P p.
exact (fun p => match p with le_n _ => IHn | le_S _ y' q' => IHS y' q' end eq_refl).
Qed.
Theorem le_unique {x y : nat} (p q : x <= y) : p = q.
revert q.
induction p as [|y p IHp] using le_ind_dependent;
intro q;
case q as [e|x' q' e] using le_case_remember.
- rewrite (nat_uip e eq_refl).
reflexivity.
- (* x = S x' but x <= x', so S x' <= x', which is a contradiction *)
exfalso.
rewrite e in q'.
exact (PeanoNat.Nat.nle_succ_diag_l _ q').
- (* S y' = x but x <= y', so S y' <= y', which is a contradiction *)
exfalso; clear IHp.
rewrite <- e in p.
exact (PeanoNat.Nat.nle_succ_diag_l _ p).
- injection e as e'.
(* We now get rid of e as equal to (f_equal S e'), and then destruct e'
now that it is an equation between variables. *)
assert (f_equal S e' = e).
+ apply nat_uip.
+ destruct H.
destruct e'.
change (le_S x y p = le_S x y q').
f_equal.
apply IHp.
Qed.
Inspired by Eqdep_dec (and with a lemma from it), I've been able to cook this proof up. The idea is that x <= y can be converted to exists k, y = k + x, and roundtripping through this conversion produces a x <= y that is indeed = to the original.
(* Existing lemmas (e.g. Nat.le_exists_sub) seem unusable (declared opaque) *)
Fixpoint le_to_add {x y : nat} (prf : x <= y) : exists k, y = k + x :=
match prf in _ <= y return exists k, y = k + x with
| le_n _ => ex_intro _ 0 eq_refl
| le_S _ y prf =>
match le_to_add prf with
| ex_intro _ k rec =>
ex_intro
_ (S k)
match rec in _ = z return S y = S z with eq_refl => eq_refl end
end
end.
Fixpoint add_to_le (x k : nat) : x <= k + x :=
match k with
| O => le_n x
| S k => le_S x (k + x) (add_to_le x k)
end.
Theorem rebuild_le
{x y : nat} (prf : x <= y)
: match le_to_add prf return x <= y with
| ex_intro _ k prf =>
match prf in _ = z return x <= z -> x <= y with
| eq_refl => fun p => p
end (add_to_le x k)
end = prf.
Proof.
revert y prf.
fix rec 2. (* induction is not enough *)
destruct prf as [ | y prf].
- reflexivity.
- specialize (rec y prf).
simpl in *.
destruct (le_to_add prf) as [k ->].
subst prf.
reflexivity.
Defined.
Then, any two x <= ys will produce the same k, by injectivity of +. The decidability of = on nat tells us that the produced equalities are also equal. Thus, the x <= ys map to the same exists k, y = k + x, and mapping that equality back tells us the x <= ys were also equal.
Theorem le_unique (x y : nat) (p q : x <= y) : p = q.
Proof.
rewrite <- (rebuild_le p), <- (rebuild_le q).
destruct (le_to_add p) as [k ->], (le_to_add q) as [l prf].
pose proof (f_equal (fun n => n - x) prf) as prf'.
simpl in prf'.
rewrite ?Nat.add_sub in prf'.
subst l.
apply K_dec with (p := prf).
+ decide equality.
+ reflexivity.
Defined.
I'm still hoping there's a better (i.e. shorter) proof available.

Show that a monic (injective) and epic (surjective) function has an inverse in Coq

A monic and epic function is an isomorphism, hence it has an inverse. I'd like a proof of that in Coq.
Axiom functional_extensionality: forall A B (f g : A->B), (forall a, f a = g a) -> f = g.
Definition compose {A B C} (f : B->C) (g: A->B) a := f (g a).
Notation "f ∘ g" := (compose f g) (at level 40).
Definition id {A} (a:A) := a.
Definition monic {A B} (f:A->B) := forall C {h k:C->A}, f ∘ h = f ∘ k -> h = k.
Definition epic {A B} (f:A->B) := forall C {h k:B->C}, h ∘ f = k ∘ f -> h = k.
Definition iso {A B} (f:A->B) := monic f /\ epic f.
Goal forall {A B} (f:A->B), iso f -> exists f', f∘f' = id /\ f'∘f = id.
The proofs I have found online (1, 2) do not give a construction of f' (the inverse). Is it possible to show this in Coq? (It is not obvious to me that the inverse is computable...)
First, a question of terminology. In category theory, an isomorphism is a morphism that has a left and a right inverse, so I am changing slightly your definitions:
Definition compose {A B C} (f : B->C) (g: A->B) a := f (g a).
Notation "f ∘ g" := (compose f g) (at level 40).
Definition id {A} (a:A) := a.
Definition monic {A B} (f:A->B) := forall C {h k:C->A}, f ∘ h = f ∘ k -> h = k.
Definition epic {A B} (f:A->B) := forall C {h k:B->C}, h ∘ f = k ∘ f -> h = k.
Definition iso {A B} (f:A->B) :=
exists g : B -> A, f ∘ g = id /\ g ∘ f = id.
It is possible to prove this result by assuming a few standard axioms, namely propositional extensionality and constructive definite description (a.k.a. the axiom of unique choice):
Require Import Coq.Logic.FunctionalExtensionality.
Require Import Coq.Logic.PropExtensionality.
Require Import Coq.Logic.Description.
Section MonoEpiIso.
Context (A B : Type).
Implicit Types (f : A -> B) (x : A) (y : B).
Definition surjective f := forall y, exists x, f x = y.
Lemma epic_surjective f : epic f -> surjective f.
Proof.
intros epic_f y.
assert (H : (fun y => exists x, f x = y) = (fun y => True)).
{ apply epic_f.
apply functional_extensionality.
intros x; apply propositional_extensionality; split.
- intros _; exact I.
- now intros _; exists x. }
now pattern y; rewrite H.
Qed.
Definition injective f := forall x1 x2, f x1 = f x2 -> x1 = x2.
Lemma monic_injective f : monic f -> injective f.
Proof.
intros monic_f x1 x2 e.
assert (H : f ∘ (fun a : unit => x1) = f ∘ (fun a : unit => x2)).
{ now unfold compose; simpl; rewrite e. }
assert (e' := monic_f _ _ _ H).
exact (f_equal (fun g => g tt) e').
Qed.
Lemma monic_epic_iso f : monic f /\ epic f -> iso f.
Proof.
intros [monic_f epic_f].
assert (Hf : forall y, exists! x, f x = y).
{ intros y.
assert (sur_f := epic_surjective _ epic_f).
destruct (sur_f y) as [x xP].
exists x; split; trivial.
intros x' x'P.
now apply (monic_injective _ monic_f); rewrite xP, x'P. }
exists (fun a => proj1_sig (constructive_definite_description _ (Hf a))).
split; apply functional_extensionality; unfold compose, id.
- intros y.
now destruct (constructive_definite_description _ (Hf y)).
- intros x.
destruct (constructive_definite_description _ (Hf (f x))); simpl.
now apply (monic_injective _ monic_f).
Qed.
End MonoEpiIso.
I believe it is not possible to prove this result without at least some form of unique choice. Assume propositional and functional extensionality. Note that, if exists! x : A, P x holds, then the unique function
{x | P x} -> unit
is both injective and surjective. (Injectivity follows from the uniqueness part, and surjectivity follows from the existence part.) If this function had an inverse for every P : A -> Type, then we could use this inverse to implement the axiom of unique choice. Since this axiom does not hold in Coq, it shouldn't be possible to build this inverse in the basic theory.

DeMorgan's law for quantifiers in Coq

I am trying to prove some FOL equivalences. I am having trouble using DeMorgan's laws for quantifiers, in particular
~ (exists x. P(x)) <-> forall x. ~P(x)
I tried applying not_ex_all_not from Coq.Logic.Classical_Pred_Type., and scoured StackOverflow (Coq convert non exist to forall statement, Convert ~exists to forall in hypothesis) but neither came close to solving the issue.
Theorem t3: forall (T: Type), forall p q: T -> Prop, forall r: T -> T -> Prop,
~(exists (x: T), ((p x) /\ (exists (y: T), ((q y) /\ ~(r x y)))))
<-> forall (x y: T), ((p x) -> (((q y) -> (r x y)))).
Proof.
intros T p q r.
split.
- intros H.
apply not_ex_all_not.
I get this error:
In environment
T : Type
p, q : T → Prop
r : T → T → Prop
H : ¬ (∃ x : T, p x ∧ (∃ y : T, q y ∧ ¬ r x y))
Unable to unify
"∀ (U : Type) (P : U → Prop), ¬ (∃ n : U, P n) → ∀ n : U, ¬ P n"
with "∀ x y : T, p x → q y → r x y".
I expected DeMorgan's law to be applied to the goal resulting in a negated existential.
Let's observe what we can derive from H:
~ (exists x : T, p x /\ (exists y : T, q y /\ ~ r x y))
=> (not exists <-> forall not)
forall x : T, ~ (p x /\ (exists y : T, q y /\ ~ r x y))
=> (not (A and B) <-> A implies not B)
forall x : T, p x -> ~ (exists y : T, q y /\ ~ r x y)
=>
forall x : T, p x -> forall y : T, ~ (q y /\ ~ r x y)
=>
forall x : T, p x -> forall y : T, q y -> ~ (~ r x y)
We end up with a double negation on the conclusion. If you don't mind using a classical axiom, we can apply NNPP to strip it and we're done.
Here is the equivalent Coq proof:
Require Import Classical.
(* I couldn't find this lemma in the stdlib, so here is a quick proof. *)
Lemma not_and_impl_not : forall P Q : Prop, ~ (P /\ Q) <-> (P -> ~ Q).
Proof. tauto. Qed.
Theorem t3: forall (T: Type), forall p q: T -> Prop, forall r: T -> T -> Prop,
~(exists (x: T), ((p x) /\ (exists (y: T), ((q y) /\ ~(r x y)))))
<-> forall (x y: T), ((p x) -> (((q y) -> (r x y)))).
Proof.
intros T p q r.
split.
- intros H x y Hp Hq.
apply not_ex_all_not with (n := x) in H.
apply (not_and_impl_not (p x)) in H; try assumption.
apply not_ex_all_not with (n := y) in H.
apply (not_and_impl_not (q y)) in H; try assumption.
apply NNPP in H. assumption.
The above was a forward reasoning. If you want backwards (by applying lemmas to the goal instead of hypotheses), things get a little harder, because you need to build the exact forms before you can apply the lemmas to the goal. This is also why your apply fails. Coq doesn't automatically find where and how to apply the lemma out of the box.
(And apply is a relatively low-level tactic. There is an advanced Coq feature that allows to apply a propositional lemma to subterms.)
Require Import Classical.
Lemma not_and_impl_not : forall P Q : Prop, ~ (P /\ Q) <-> (P -> ~ Q).
Proof. tauto. Qed.
Theorem t3: forall (T: Type), forall p q: T -> Prop, forall r: T -> T -> Prop,
~(exists (x: T), ((p x) /\ (exists (y: T), ((q y) /\ ~(r x y)))))
<-> forall (x y: T), ((p x) -> (((q y) -> (r x y)))).
Proof.
intros T p q r.
split.
- intros H x y Hp Hq.
apply NNPP. revert dependent Hq. apply not_and_impl_not.
revert dependent y. apply not_ex_all_not.
revert dependent Hp. apply not_and_impl_not.
revert dependent x. apply not_ex_all_not. apply H.
Actually, there is an automation tactic called firstorder, which (as you guessed) solves first-order intuitionistic logic. Note that NNPP is still needed since firstorder doesn't handle classical logic.
Theorem t3: forall (T: Type), forall p q: T -> Prop, forall r: T -> T -> Prop,
~(exists (x: T), ((p x) /\ (exists (y: T), ((q y) /\ ~(r x y)))))
<-> forall (x y: T), ((p x) -> (((q y) -> (r x y)))).
Proof.
intros T p q r.
split.
- intros H x y Hp Hq. apply NNPP. firstorder.
- firstorder. Qed.

How to give a counterxample in Coq?

Is it possible to give a counterexample for a statement which doesn't hold in general? Like, for example that the all quantor does not distribute over the connective "or". How would you state that to begin with?
Parameter X : Set.
Parameter P : X -> Prop.
Parameter Q : X -> Prop.
(* This holds in general *)
Theorem forall_distributes_over_and
: (forall x:X, P x /\ Q x) -> ((forall x:X, P x) /\ (forall x:X, Q x)).
Proof.
intro H. split. apply H. apply H.
Qed.
(* This doesn't hold in general *)
Theorem forall_doesnt_distributes_over_or
: (forall x:X, P x \/ Q x) -> ((forall x:X, P x) \/ (forall x:X, Q x)).
Abort.
Here is a quick and dirty way to prove something similar to what you want:
Theorem forall_doesnt_distributes_over_or:
~ (forall X P Q, (forall x:X, P x \/ Q x) -> ((forall x:X, P x) \/ (forall x:X, Q x))).
Proof.
intros H.
assert (X : forall x : bool, x = true \/ x = false).
destruct x; intuition.
specialize (H _ (fun b => b = true) (fun b => b = false) X).
destruct H as [H|H].
now specialize (H false).
now specialize (H true).
Qed.
I have to quantify X P and Q inside the negation in order to be able to provide the one I want. You couldn't quite do that with your Parameters as they somehow fixed an abstract X, P and Q, thus making your theorem potentially true.
In general, if you want to produce a counterexample, you can state the negation of the formula and then prove that this negation is satisfied.