I am learning Homotopic Type Theory (HoTT) and its relation to COQ.
Especially the path induction concept of the identity type is still mysterious to me.
Therefore I made some experiments with COQ.
Let's start with a simple Lemma for the standard equality type using path induction:
Lemma eq_sym: forall (x y:nat), x = y -> y = x.
intros.
apply (match H in (_ = y0) return y0 = x with eq_refl => eq_refl end).
Defined.
Now let's see if that is a special handling of the COQ "eq" type. Therefore let's define a new equality type (only for nat) with an analogous symmetry lemma:
Inductive est (x : nat) : nat -> Prop :=
est_refl: est x x.
Lemma est_sym: forall (x y:nat), est x y -> est y x.
intros.
apply (match H in (est _ y0) return est y0 x with est_refl => est_refl x end).
Defined.
Ok, this works in the same way like the standard "=" type.
Now let's generalise it:
Inductive tri (x : nat) : nat->nat->Prop :=
tri_refl: tri x x x.
Lemma tri_sym: forall (x y z:nat), tri x y z -> tri z x y.
intros.
apply (match H in (tri _ y0 z0) return tri z0 x y0 with tri_refl => tri_refl x end).
Defined.
My question is:
How does this relate to the theory of HoTT?
Is this a generalised path induction which is not part of HoTT?
Your "three-ended equality" is equivalent to a pair of equality proofs, in the sense that we can write Coq functions that convert back and forth between the two forms. (These are the eq_of_teq and teq_of_eq terms in the excerpt below.)
Section Teq.
Variable T : Type.
Inductive teq (x : T) : T -> T -> Prop :=
| teq_refl : teq x x x.
Definition teq_of_eq {x y z} (e1 : x = y) (e2 : x = z) : teq x y z :=
match e1 in _ = y' return x = z -> teq x y' z with
| eq_refl => fun e2 =>
match e2 in _ = z' return teq x x z' with
| eq_refl => teq_refl x
end
end e2.
Definition eq_of_teq1 {x y z} (te : teq x y z) : x = y :=
match te in teq _ y' z' return x = y' with
| teq_refl => eq_refl
end.
Definition eq_of_teq2 {x y z} (te : teq x y z) : x = z :=
match te in teq _ y' z' return x = z' with
| teq_refl => eq_refl
end.
Definition teq_eq_teq x y z (te : teq x y z) :
teq_of_eq (eq_of_teq1 te) (eq_of_teq2 te) = te :=
match te as te' in teq _ y' z' return teq_of_eq (eq_of_teq1 te') (eq_of_teq2 te') = te' with
| teq_refl => eq_refl
end.
Definition eq_teq_eq1 x y z (e1 : x = y) (e2 : x = z) :
eq_of_teq1 (teq_of_eq e1 e2) = e1 :=
match e1 as e1' in _ = y' return eq_of_teq1 (teq_of_eq e1' e2) = e1' with
| eq_refl =>
match e2 as e2' in _ = z' return eq_of_teq1 (teq_of_eq eq_refl e2') = eq_refl with
| eq_refl => eq_refl
end
end.
Definition eq_teq_eq2 x y z (e1 : x = y) (e2 : x = z) :
eq_of_teq2 (teq_of_eq e1 e2) = e2 :=
match e1 as e1' in _ = y' return eq_of_teq2 (teq_of_eq e1' e2) = e2 with
| eq_refl =>
match e2 as e2' in _ = z' return eq_of_teq2 (teq_of_eq eq_refl e2') = e2' with
| eq_refl => eq_refl
end
end.
End Teq.
The teq_eq_teq, eq_teq_eq1 and eq_teq_eq2 lemmas show that the converting back and forth does not change the terms we start with; hence, both representations are equivalent. In this sense, Coq does not have more expressive power than HoTT just because we can define a teq.
In basic Martin-Löf type theory (the formal system upon which HoTT is based), you don't gain much by carrying a pair of equalities between the same terms, since the only thing you can do with such equalities is to perform casts on the types of the terms you manipulate. Thus, it doesn't really matter usually whether you have only one or two equality proofs between terms.
The situation changes a little bit in HoTT because of the addition of the univalence axiom, which allows us to use equality proofs between types in a computationally interesting way. This is because a proof of A = B in HoTT can be any bijection between the types A = B. In that setting, a proof of teq A B C would be equivalent to two bijections: one between A and B, and the other one between A and C.
Related
As an exercise I want to prove that there is always exists a maximum element in a non-empty sequence.
Theorem largest_el_in_list (s: seq rat) x : x \in s -> exists y, y \in s /\ forall z, z \in s -> y >= z.
My idea was to go by induction on s, and then to destruct x. The largest element in this first case is the only element in the list. However, in the second case I'm getting quite confused. Am I supposed to use the inductive hypothesis? My idea was that we can get rid of the existential by exists max a a1, where a is the maximal element we found in the previous step, and a1 is the new element being added to the sequence. But if I do this, then I can't use the inductive hypothesis and I get completely stuck.
I've been stuck for hours and would love to know if I have the right idea.
Edit:
Here is the proof so far, with the current proof state below:
Theorem largest_el_in_list (s: seq rat) x : x \in s -> exists y, y \in s /\ forall z, z \in s -> y >= z.
Proof.
elim/last_ind : s => //= s x0 IH x_in_rcons.
case : s IH x_in_rcons.
move => _ x_in_sx0.
exists x0. split.
rewrite in_cons. apply/orP. left. by apply/eqP.
rewrite in_cons in x_in_sx0. case/orP : x_in_sx0 => //= xeqx0 z z_in_sx0.
rewrite in_cons in z_in_sx0. case/orP : z_in_sx0 => //= zeqx0. case/eqP : zeqx0 => zeqx0.
by rewrite zeqx0.
move => a l IH x_in_rcons.
exists (maxr x0 a). split.
have [agex0 | x0gta] := (lerP x0 a).
by rewrite mem_head.
rewrite rcons_cons in_cons. apply/orP. right. by rewrite in_rcons.
move => z z_in_rcons.
Admitted.
Proof state:
x: rat_eqType
x0, a: rat
l: seq rat
IH: x \in a :: l ->
exists y : rat, y \in a :: l /\ (forall z : rat, z \in a :: l -> z <= y)
x_in_rcons: x \in rcons (a :: l) x0
z: rat
z_in_rcons: z \in rcons (a :: l) x0
-------------------------------
(1/1)
z <= maxr x0 a
ANOTHER EDIT:
Imports:
From finprob Require Import prob.
From mathcomp Require Import all_ssreflect all_algebra seq.
From extructures Require Import ord fset fmap ffun.
Import Num.Theory.
Import GRing.
Import Bool.
Import Num.Def.
You can find extructures here https://github.com/arthuraa/extructures
And finprob here https://github.com/arthuraa/finprob
UPDATE:
Although changing the definition clears the path, there is still an issue. Here is the updated proof and proof state:
Theorem largest_el_in_list (s: seq rat) : s != [::] -> exists y, y \in s /\ forall z, z \in s -> y >= z.
Proof.
elim/last_ind : s => //= s x0 IH x_in_rcons.
case : s IH x_in_rcons.
move => _ x_in_sx0.
exists x0. split.
rewrite in_cons. apply/orP. left. by apply/eqP.
move => z z_in_cons.
rewrite in_cons in z_in_cons.
case/orP : z_in_cons => //= zeqx0.
case/eqP : zeqx0 => zeqx0. by rewrite zeqx0.
move => a l IH cons_nempty.
case IH => //= x [x_in_cons IH'].
exists x.
split.
rewrite in_cons in x_in_cons.
rewrite in_cons.
case/orP : x_in_cons => [xeqa | x_in_l].
by rewrite xeqa orTb. apply/orP. right. rewrite in_rcons. apply/orP. by right.
move => z z_in_cons.
apply IH'.
rewrite in_cons. rewrite in_cons in z_in_cons.
case/orP : z_in_cons => [zeqa | z_in_cons].
by rewrite zeqa orTb.
Admitted.
x0, a: rat
l: seq rat
IH: a :: l != [::] ->
exists y : rat, y \in a :: l /\ (forall z : rat, z \in a :: l -> z <= y)
cons_nempty: rcons (a :: l) x0 != [::]
x: rat
x_in_cons: x \in a :: l
IH': forall z : rat, z \in a :: l -> z <= x
z: rat
z_in_cons: z \in rcons l x0
-------------------------
(1/1)
(z == a) || (z \in l)
I don't see how this can be true, because we know from z_in_cons that z is either equal to x0, or it is in l. Thus, if we go by cases, the first case is impossible because we are lacking some information about x0.
Another approach would be to explicitly provide, right from the start, a value for y, the existence of which you are looking for. This should be the maximum of the list, which you could either specify yourself, via a Fixpoint definition, or be the maximum as defined in Coq.
But if you want to keep the proof by induction, as you suggest in your comment, here is one way (I suppose one can write it in a more concise manner). I'm using here nat instead of rat, for convenience:
Theorem largest_el_in_list (s: seq nat) :
s != [::] -> exists y, y \in s /\ forall z, z \in s -> y >= z.
Proof.
elim: s => [//=|n s IH _].
have [/eqP ->|/IH [max [maxins ismax]]] := boolP (s == nil).
- exists n.
split=> [|y]; first by rewrite in_cons eq_refl orTb.
by rewrite in_cons ?in_nil => /orP [/eqP ->|].
- have [lenx|] := boolP (n <= max).
- exists max.
split=> [|z]; first by rewrite in_cons maxins orbT.
by rewrite in_cons => /orP [/eqP ->|/ismax].
- rewrite -ltnNge.
exists n.
split=> [|z]; first by rewrite in_cons eq_refl orTb.
rewrite in_cons => /orP [/eqP -> //|/ismax ltzx].
by rewrite (#leq_trans max) // ltnW.
Qed.
May be the issue comes from the variable x.
A possible fix is to have x universally quantified, e.g. by starting the proof with a move: x (before the induction on s).
Another solution would be to remove x, which occurs only once in your statement and makes the subgoals hard to read, and prove instead:
Theorem largest_el_in_list (s: seq rat) : s <> nil ->
exists y, y \in s /\ forall z, z \in s -> y >= z.
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.
I am trying to implement/specify the permutation groups (symmetric groups) in coq. This went well for a bit, until I tried to prove that the identity is actually the identity. My proof gets stuck on proving that the proposition "x is invertible" is exactly the same as the proposition "id * x is invertible".
Are these two propositions actually the same? Am I trying to prove something that is not true? Is there a better way of specifying the permutation group (as a type)?
(* The permutation group on X contains all functions between X and X that are bijective/invertible *)
Inductive G {X : Type} : Type :=
| function (f: X -> X) (H: exists g: X -> X, forall x : X, f (g x) = x /\ g (f x) = x).
(* Composing two functions preserves invertibility *)
Lemma invertible_composition {X : Type} (f g: X -> X) :
(exists f' : X -> X, forall x : X, f (f' x) = x /\ f' (f x) = x) ->
(exists g' : X -> X, forall x : X, g (g' x) = x /\ g' (g x) = x) ->
exists h : X -> X, forall x : X, (fun x => f (g x)) (h x) = x /\ h ((fun x => f (g x)) x) = x.
Admitted.
(* The group operation is composition *)
Definition op {X : Type} (a b : G) : G :=
match a, b with
| function f H, function g H' => function (fun x => f (g x)) (#invertible_composition X f g H H')
end.
Definition id' {X : Type} (x : X) : X := x.
(* The identity function is invertible *)
Lemma id_invertible {X : Type} : exists g : X -> X, forall x : X, id' (g x) = x /\ g (id' x) = x.
Admitted.
Definition id {X : Type} : (#G X) := function id' id_invertible.
(* The part on which I get stuck: proving that composition with the identity does not change elements. *)
Lemma identity {X: Type} : forall x : G, op id x = x /\ #op X x id = x.
Proof.
intros.
split.
- destruct x.
simpl.
apply f_equal.
Abort.
I believe that your statement cannot be proved without assuming extra axioms:
proof_irrelevance:
forall (P : Prop) (p q : P), p = q.
You need this axiom to show that two elements of G are equal when the underlying functions are:
Require Import Coq.Logic.ProofIrrelevance.
Inductive G X : Type :=
| function (f: X -> X) (H: exists g: X -> X, forall x : X, f (g x) = x /\ g (f x) = x).
Arguments function {X} _ _.
Definition fun_of_G {X} (f : G X) : X -> X :=
match f with function f _ => f end.
Lemma fun_of_G_inj {X} (f g : G X) : fun_of_G f = fun_of_G g -> f = g.
Proof.
destruct f as [f fP], g as [g gP].
simpl.
intros e.
destruct e.
f_equal.
apply proof_irrelevance.
Qed.
(As a side note, it is usually better to declare the X parameter of G explicitly, rather than implicitly. It is rarely the case that Coq can figure out what X should be on its own.)
With fun_of_G_inj, it should be possible to show identity simply by applying it to each equality, because fun a => (fun x => x) (g a) is equal to g for any g.
If you want to use this representation for groups, you'll probably also need the axiom of functional extensionality eventually:
functional_extensionality:
forall X Y (f g : X -> Y), (forall x, f x = g x) -> f = g.
This axiom is available in the Coq.Logic.FunctionalExtensionality module.
If you want to define the inverse element as a function, you probably also need some form of the axiom of choice: it is necessary for extracting the inverse element g from the existence proof.
If you don't want to assume extra axioms, you have to place restrictions on your permutation group. For instance, you can restrict your attention to elements with finite support -- that is, permutation that fix all elements of X, except for a finite set. There are multiple libraries that allow you to work with permutations this way, including my own extensional structures.
I'm new to Coq and am doing some exercises to get more familiar with it.
My understanding is that proving a proposition in Coq "really" is writing down a type in Gallina and then showing that it's inhabited using tactics to combine terms together in deterministic ways.
I'm wondering if there's a way to get a pretty-printed representation of the actual term, with all the tactics removed.
In the example below, an anonymous term of type plus_comm (x y : N) : plus x y = plus y x is ultimately produced... I think. What should I do if I want to look at it? In a certain sense, I'm curious what the tactics "desugar" to.
Here's the code in question, lifted essentially verbatim from a tutorial on YouTube https://www.youtube.com/watch?v=OaIn7g8BAIc.
Inductive N : Type :=
| O : N
| S : N -> N
.
Fixpoint plus (x y : N) : N :=
match x with
| O => y
| S x' => S (plus x' y)
end.
Lemma plus_0 (x : N) : plus x O = x.
Proof.
induction x.
- simpl. reflexivity.
- simpl. rewrite IHx. reflexivity.
Qed.
Lemma plus_S (x y : N) : plus x (S y) = S(plus x y).
Proof.
induction x.
- simpl. reflexivity.
- simpl. rewrite IHx. reflexivity.
Qed.
Lemma plus_comm (x y : N) : plus x y = plus y x.
Proof.
induction x.
- simpl. rewrite plus_0. reflexivity.
- simpl. rewrite IHx. rewrite plus_S. reflexivity.
Qed.
First of all, plus_comm is not a part of the type. You get a term named plus_comm of type forall x y : N, plus x y = plus y x. You can check it using the following command
Check plus_comm.
So, an alternative way of defining the plus_comm lemma is
Lemma plus_comm : forall x y : N, plus x y = plus y x.
As a side note: in this case you'll need to add intros x y. (or just intros.) after the Proof. part.
Tactics (and the means to glue them together) are a metalanguage called Ltac, because they are used to produce terms of another language, called Gallina, which is the specification language of Coq.
For example, forall x y : N, plus x y = plus y x is an instance of Gallina sentence as well as the body of the plus function. To obtain the term attached to plus_comm use the Print command:
Print plus_comm.
plus_comm =
fun x y : N =>
N_ind (fun x0 : N => plus x0 y = plus y x0)
(eq_ind_r (fun n : N => y = n) eq_refl (plus_0 y))
(fun (x0 : N) (IHx : plus x0 y = plus y x0) =>
eq_ind_r (fun n : N => S n = plus y (S x0))
(eq_ind_r (fun n : N => S (plus y x0) = n) eq_refl (plus_S y x0))
IHx) x
: forall x y : N, plus x y = plus y x
It is not an easy read, but with some experience you'll be able to understand it.
Incidentally, here is how we could have proved the lemma not using tactics:
Definition plus_comm : forall x y : N, plus x y = plus y x :=
fix IH (x y : N) :=
match x return plus x y = plus y x with
| O => eq_sym (plus_0 y)
| S x => eq_ind _ (fun p => S p = plus y (S x)) (eq_sym (plus_S y x)) _ (eq_sym (IH x y))
end.
To explain a few things: fix is the means of defining recursive functions, eq_sym is used to change x = y into y = x, and eq_ind corresponds to the rewrite tactic.
I have two elements f : X -> bool and x : X.
How to define g : X -> bool such g x = true and g y = f y for y != x.
Following your answer to my comment, I don't think you can define a "function" g, because you need a constructive way do distinguish x from other instances of type X. However you could define a relation between the two, which could be transformed into a function if you get decidability.
Something like:
Parameter X : Type.
Parameter f : X -> bool.
Parameter x : X.
Inductive gRel : X -> bool -> Prop :=
| is_x : gRel x true
| is_not_x : forall y: X, y <> x -> gRel y (f y)
.
Definition gdec (h: forall a b: X, {a = b}+{a <> b}) : X -> bool :=
fun a => if h a x then true else f a.
Lemma gRel_is_a_fun: (forall a b: X, {a = b}+{a <> b}) ->
exists g : X -> bool, forall a, gRel a (g a).
Proof.
intro hdec.
exists (gdec hdec); unfold gdec.
intro a; destruct (hdec a x).
now subst; apply is_x.
now apply is_not_x.
Qed.
Just complementing Vinz's answer, there's no way of defining such a function for arbitrary X, because it implies that X has "almost decidable" equality:
Section Dec.
Variable X : Type.
Variable override : (X -> bool) -> X -> X -> bool.
Hypothesis Hoverride_eq : forall f x, override f x x = true.
Hypothesis Hoverride_neq : forall f x x', x <> x' -> override f x x' = f x'.
Lemma xeq_dec' (x x' : X) : {~ x <> x'} + {x <> x'}.
Proof.
destruct (override (fun _ => false) x x') eqn:E.
- left.
intros contra.
assert (H := Hoverride_neq (fun _ => false) _ _ contra).
simpl in H.
congruence.
- right.
intros contra.
subst x'.
rewrite Hoverride_eq in E.
discriminate.
Qed.
End Dec.
This lemma says that if there's a way of doing what you asked for for X, then one can test whether two elements x and x' of X are equal, except that the proof of equality that one gets in the true case is actually a proof of the double negation of x = x'.