I'd like to define a function with two, higher inductive typed, arguments in Cubical mode. I am using the cubical package as my "prelude" library.
I first define a quotient type for integers as a HIT:
{-# OPTIONS --cubical #-}
module _ where
open import Data.Nat renaming (_+_ to _+̂_)
open import Cubical.Core.Prelude
data ℤ : Set where
_-_ : (x : ℕ) → (y : ℕ) → ℤ
quot : ∀ {x y x′ y′} → (x ℕ+ y′) ≡ (x′ ℕ+ y) → (x - y) ≡ (x′ - y′)
I can then define a unary function using pattern matching:
_+1 : ℤ → ℤ
(x - y) +1 = suc x - y
quot {x} {y} prf i +1 = quot {suc x} {y} (cong suc prf) i
So far, so good. But what if I want to define a binary function, such as addition?
First, let's get the boring arithmetic proofs out of the way:
import Data.Nat.Properties
open Data.Nat.Properties.SemiringSolver
using (prove; solve; _:=_; con; var; _:+_; _:*_; :-_; _:-_)
open import Relation.Binary.PropositionalEquality renaming (refl to prefl; _≡_ to _=̂_) using ()
fromPropEq : ∀ {ℓ A} {x y : A} → _=̂_ {ℓ} {A} x y → x ≡ y
fromPropEq prefl = refl
open import Function using (_$_)
reorder : ∀ x y a b → (x +̂ a) +̂ (y +̂ b) ≡ (x +̂ y) +̂ (a +̂ b)
reorder x y a b = fromPropEq $ solve 4 (λ x y a b → (x :+ a) :+ (y :+ b) := (x :+ y) :+ (a :+ b)) prefl x y a b
inner-lemma : ∀ x y a b a′ b′ → a +̂ b′ ≡ a′ +̂ b → (x +̂ a) +̂ (y +̂ b′) ≡ (x +̂ a′) +̂ (y +̂ b)
inner-lemma x y a b a′ b′ prf = begin
(x +̂ a) +̂ (y +̂ b′) ≡⟨ reorder x y a b′ ⟩
(x +̂ y) +̂ (a +̂ b′) ≡⟨ cong (x +̂ y +̂_) prf ⟩
(x +̂ y) +̂ (a′ +̂ b) ≡⟨ sym (reorder x y a′ b) ⟩
(x +̂ a′) +̂ (y +̂ b) ∎
outer-lemma : ∀ x y x′ y′ a b → x +̂ y′ ≡ x′ +̂ y → (x +̂ a) +̂ (y′ +̂ b) ≡ (x′ +̂ a) +̂ (y +̂ b)
outer-lemma x y x′ y′ a b prf = begin
(x +̂ a) +̂ (y′ +̂ b) ≡⟨ reorder x y′ a b ⟩
(x +̂ y′) +̂ (a +̂ b) ≡⟨ cong (_+̂ (a +̂ b)) prf ⟩
(x′ +̂ y) +̂ (a +̂ b) ≡⟨ sym (reorder x′ y a b) ⟩
(x′ +̂ a) +̂ (y +̂ b) ∎
I now try to define _+_ using pattern matching, but I have no idea how to handle the "points in the center of the face", so to speak:
_+_ : ℤ → ℤ → ℤ
(x - y) + (a - b) = (x +̂ a) - (y +̂ b)
(x - y) + quot {a} {b} {a′} {b′} eq₂ j = quot {x +̂ a} {y +̂ b} {x +̂ a′} {y +̂ b′} (inner-lemma x y a b a′ b′ eq₂) j
quot {x} {y} {x′} {y′} eq₁ i + (a - b) = quot {x +̂ a} {y +̂ b} {x′ +̂ a} {y′ +̂ b} (outer-lemma x y x′ y′ a b eq₁) i
quot {x} {y} {x′} {y′} eq₁ i + quot {a} {b} {a′} {b′} eq₂ j = ?
So basically what I have is the following situation:
p Xᵢ
X ---------+---> X′
p₀ i
A X+A --------\---> X′+A
| | |
q| q₀ | | qᵢ
| | |
Aⱼ + j+ [+] <--- This is where we want to get to!
| | |
V V p₁ |
A′ X+A′ -------/---> X′+A′
i
with
X = (x - y)
X′ = (x′ - y′)
A = (a - b)
A′ = (a′ - b′)
p : X ≡ X′
p = quot eq₁
q : A ≡ A′
q = quot eq₂
p₀ : X + A ≡ X′ + A
p₀ = quot (outer-lemma x y x′ y′ a b eq₁)
p₁ : X + A′ ≡ X′ + A′
p₁ = quot (outer-lemma x y x′ y′ a′ b′ eq₁)
q₀ : X + A ≡ X + A′
q₀ = quot (inner-lemma x y a b a′ b′ eq₂)
q₁ : X′ + A ≡ X′ + A′
q₁ = quot (inner-lemma x′ y′ a b a′ b′ eq₂)
I am using this construction to push out q₀ horizontally by i:
slidingLid : ∀ {ℓ} {A : Set ℓ} {a b c d} (p₀ : a ≡ b) (p₁ : c ≡ d) (q : a ≡ c) → ∀ i → p₀ i ≡ p₁ i
slidingLid p₀ p₁ q i j = comp (λ _ → A)
(λ{ k (i = i0) → q j
; k (j = i0) → p₀ (i ∧ k)
; k (j = i1) → p₁ (i ∧ k)
})
(inc (q j))
and using this, my attempt at + is as follows:
quot {x} {y} {x′} {y′} eq₁ i + quot {a} {b} {a′} {b′} eq₂ j = Xᵢ+Aⱼ
where
X = (x - y)
X′ = (x′ - y′)
A = (a - b)
A′ = (a′ - b′)
p : X ≡ X′
p = quot eq₁
q : A ≡ A′
q = quot eq₂
p₀ : X + A ≡ X′ + A
p₀ = quot (outer-lemma x y x′ y′ a b eq₁)
p₁ : X + A′ ≡ X′ + A′
p₁ = quot (outer-lemma x y x′ y′ a′ b′ eq₁)
q₀ : X + A ≡ X + A′
q₀ = quot (inner-lemma x y a b a′ b′ eq₂)
qᵢ : ∀ i → p₀ i ≡ p₁ i
qᵢ = slidingLid p₀ p₁ q₀
q₁ : X′ + A ≡ X′ + A′
q₁ = quot (inner-lemma x′ y′ a b a′ b′ eq₂)
Xᵢ+Aⱼ = qᵢ i j
But this fails with the following type error:
quot (inner-lemma x′ y′ a b a′ b′ eq₂) j !=
hcomp
(λ { i ((~ i1 ∨ ~ j ∨ j) = i1)
→ transp (λ j₁ → ℤ) i
((λ { i₁ (i1 = i0) → q₀ eq₁ i1 eq₂ j j
; i₁ (j = i0) → p₀ eq₁ i1 eq₂ j (i1 ∧ i₁)
; i₁ (j = i1) → p₁ eq₁ i1 eq₂ j (i1 ∧ i₁)
})
(i ∨ i0) _)
})
(transp (λ _ → ℤ) i0 (ouc (inc (q₀ eq₁ i1 eq₂ j j))))
of type ℤ
One hint to what might be going wrong is that while these three sides degenerate nicely:
top : ∀ i → qᵢ i i0 ≡ p i + q i0
top i = refl
bottom : ∀ i → qᵢ i i1 ≡ p i + q i1
bottom i = refl
left : qᵢ i0 ≡ q₀
left = refl
the rightmost side doesn't:
right : qᵢ i1 ≡ q₁
right = ? -- refl fails here
I guess because qᵢ is pulled from the left side, so there could still be a hole between the right side and the pushed-all-the-way qᵢ, i.e. this would still be possible, with a hole at O between qᵢ i1 and q₁:
p₀
X+A ------------> X′+A
| /|
q₀ | / | q₁
| | |
| | O|
| \ |
V p₁ \|
X+A′ -----------> X′+A′
and intiutively it makes sense, because q₁ is some algebraic statement about natural numbers, and qᵢ i1 is a continuously deformed version of a different algebraic statement about different natural numbers, so there still has to be some kind of connection made between the two; but I don't know where to start on making that connection (i.e. constructing explicitly the 2-path between qᵢ i1 and q₁)
It turns out there really was a possibility of a hole between qᵢ i1 and q₁ with the formalization I've been trying to do. The solution hit me when I went back to the HoTT book to try and solve this more abstractly for all quotient types, not just this particular ℤ type. To quote from section 6.10:
We can also describe this directly, as the higher inductive type A/R
generated by
A function q : A → A/R;
For each a, b : A such that R(a, b), an equality q(a) = q(b); and
The 0-truncation constructor: for all x, y : A/R and r,s : x = y, we have r = s.
So what I was missing was that third point: that the lack of higher-typed structure is something that needs to be explicitly modeled.
Using this information, I have added a third constructor to my ℤ:
Same : ℕ → ℕ → ℕ → ℕ → Set
Same x y x′ y′ = x +̂ y′ ≡ x′ +̂ y
data ℤ : Set where
_-_ : (x : ℕ) → (y : ℕ) → ℤ
quot : ∀ {x y x′ y′} → Same x y x′ y′ → (x - y) ≡ (x′ - y′)
trunc : {x y : ℤ} → (p q : x ≡ y) → p ≡ q
This allowed me to prove right (and thus, surface) with no further issues. One slight hiccup is that trying to use pattern matching caused some weird "function is not fibrant" errors, so I ended up going via the following explicit eliminator:
module ℤElim {ℓ} {P : ℤ → Set ℓ}
(point* : ∀ x y → P (x - y))
(quot* : ∀ {x y x′ y′} same → PathP (λ i → P (quot {x} {y} {x′} {y′} same i)) (point* x y) (point* x′ y′))
(trunc* : ∀ {x y} {p q : x ≡ y} → ∀ {fx : P x} {fy : P y} (eq₁ : PathP (λ i → P (p i)) fx fy) (eq₂ : PathP (λ i → P (q i)) fx fy) → PathP (λ i → PathP (λ j → P (trunc p q i j)) fx fy) eq₁ eq₂)
where
ℤ-elim : ∀ x → P x
ℤ-elim (x - y) = point* x y
ℤ-elim (quot p i) = quot* p i
ℤ-elim (trunc p q i j) = trunc* (cong ℤ-elim p) (cong ℤ-elim q) i j
and so for reference, the full implementation of _+_ using ℤ-elim:
_+_ : ℤ → ℤ → ℤ
_+_ = ℤ-elim
(λ x y → ℤ-elim
(λ a b → (x +̂ a) - (y +̂ b))
(λ eq₂ → quot (inner-lemma x y eq₂))
trunc)
(λ {x} {y} {x′} {y′} eq₁ i → ℤ-elim
(λ a b → quot (outer-lemma x y eq₁) i)
(λ {a} {b} {a′} {b′} eq₂ j → lemma {x} {y} {x′} {y′} {a} {b} {a′} {b′} eq₁ eq₂ i j )
trunc)
(λ {_} {_} {_} {_} {x+} {y+} eq₁ eq₂ i →
funExt λ a → λ j → trunc {x+ a} {y+ a} (ap eq₁ a) (ap eq₂ a) i j)
where
lemma : ∀ {x y x′ y′ a b a′ b′} → Same x y x′ y′ → Same a b a′ b′ → I → I → ℤ
lemma {x} {y} {x′} {y′} {a} {b} {a′} {b′} eq₁ eq₂ i j = surface i j
where
{-
p Xᵢ
X ---------+---> X′
p₀ i
A X+A --------\---> X′+A
| | |
q| q₀ | | qᵢ
| | |
Aⱼ + j+ [+] <--- This is where we want to get to!
| | |
V V p₁ |
A′ X+A′ -------/---> X′+A′
i
-}
X = x - y
X′ = x′ - y′
A = a - b
A′ = a′ - b′
X+A = (x +̂ a) - (y +̂ b)
X′+A = (x′ +̂ a) - (y′ +̂ b)
X+A′ = (x +̂ a′) - (y +̂ b′)
X′+A′ = (x′ +̂ a′) - (y′ +̂ b′)
p : X ≡ X′
p = quot eq₁
q : A ≡ A′
q = quot eq₂
p₀ : X+A ≡ X′+A
p₀ = quot (outer-lemma x y eq₁)
p₁ : X+A′ ≡ X′+A′
p₁ = quot (outer-lemma x y eq₁)
q₀ : X+A ≡ X+A′
q₀ = quot (inner-lemma x y eq₂)
q₁ : X′+A ≡ X′+A′
q₁ = quot (inner-lemma x′ y′ eq₂)
qᵢ : ∀ i → p₀ i ≡ p₁ i
qᵢ = slidingLid p₀ p₁ q₀
left : qᵢ i0 ≡ q₀
left = refl
right : qᵢ i1 ≡ q₁
right = trunc (qᵢ i1) q₁
surface : PathP (λ i → p₀ i ≡ p₁ i) q₀ q₁
surface i = comp (λ j → p₀ i ≡ p₁ i)
(λ { j (i = i0) → left j
; j (i = i1) → right j
})
(inc (qᵢ i))
This is a partial answer, in the hope that it will lure someone into solving the next piece of this puzzle.
So, I managed to prove right, and with it, that there is a continuous surface from left to right :
right : qᵢ i1 ≡ q₁
right i = comp
(λ j → p j + A ≡ p j + A′)
(λ { j (i = i0) → qᵢ j
; j (i = i1) → cong (λ ξ → quot {x} {y} {x′} {y′} eq₁ j + ξ) q
}
(inc (left i))
surface : PathP (λ i → p₀ i ≡ p₁ i) q₀ q₁
surface i = comp (λ j → p₀ i ≡ p₁ i)
(λ { j (i = i0) → q₀
; j (i = i1) → right j
})
(inc (qᵢ i))
Xᵢ+Aⱼ = surface i j
This definition of Xᵢ+Aⱼ passes the typechecker, but fails during termination checking. Basically, all occurrences of _+_ are marked as problematic; in particular, the ones in the definition of right: both the p j + A and p j + A′ calls, and the congruence function in cong (λ ξ → quot {x} {y} {x′} {y′} eq₁ j + ξ) q.
The first two don't make much sense to me: I have already defined _+- for the midpoint+point and the point+point cases, and the second argument in p j + A and p j + A′ are clearly points.
The third one, I am looking for suggestions on.
Related
The PLFA exercise: what if we write the arithmetic more "naturally" in Quantifiers chapter (https://plfa.github.io/Quantifiers/) ?
∃-even′ : ∀ {n : ℕ} → ∃[ m ] ( 2 * m ≡ n) → even n
∃-odd′ : ∀ {n : ℕ} → ∃[ m ] (2 * m + 1 ≡ n) → odd n
I have make the type right. But have got Termination checking failed for the following functions:
dbl≡2* : ∀ n → n + n ≡ 2 * n
dbl≡2* n = cong (n +_) (sym (+-identityʳ n))
+-suc1 : ∀ (m : ℕ) → m + 1 ≡ suc m
+-suc1 m =
begin
m + 1
≡⟨⟩
m + (suc zero)
≡⟨ +-suc m zero ⟩
suc (m + zero)
≡⟨ cong suc (+-identityʳ m) ⟩
suc m
∎
help1 : ∀ m → 2 * m + 1 ≡ suc (m + m)
help1 m =
begin
2 * m + 1
≡⟨ sym ( cong (_+ 1) (dbl≡2* m) ) ⟩
m + m + 1 -- must use every rule
≡⟨ +-assoc m m 1 ⟩
m + (m + 1)
≡⟨ cong (m +_) (+-suc1 m) ⟩
m + suc m
≡⟨ +-suc m m ⟩
suc (m + m)
∎
∃-even′ ⟨ zero , refl ⟩ = even-zero
∃-even′ ⟨ suc m , refl ⟩ rewrite +-identityʳ m
| +-suc m m
= even-suc (∃-odd′ ⟨ (m) , help1 m ⟩)
∃-odd′ ⟨ m , refl ⟩ rewrite +-suc (2 * m) 0
| +-identityʳ m
| +-identityʳ (m + m)
| dbl≡2* m
= odd-suc (∃-even′ ⟨ m , refl ⟩)
For the normal version, the same mutually-recursive define can work fine.
∃-even : ∀ {n : ℕ} → ∃[ m ] ( m * 2 ≡ n) → even n
∃-odd : ∀ {n : ℕ} → ∃[ m ] (1 + m * 2 ≡ n) → odd n
∃-even ⟨ zero , refl ⟩ = even-zero
∃-even ⟨ suc x , refl ⟩ = even-suc (∃-odd ⟨ x , refl ⟩)
∃-odd ⟨ x , refl ⟩ = odd-suc (∃-even ⟨ x , refl ⟩)
∃-even′ ⟨ zero , refl ⟩ = even-zero
∃-even′ ⟨ suc m , refl ⟩ rewrite +-identityʳ m
| +-suc m m
= even-suc (∃-odd′ ⟨ m , help1 m ⟩)
∃-odd′ ⟨ m , refl ⟩ rewrite +-suc (2 * m) 0
| +-identityʳ m
| +-identityʳ (m + m)
| dbl≡2* m
= odd-suc (∃-even′ ⟨ m , refl ⟩)
Your recursive calls are:
∃-even′ ⟨ suc m , refl ⟩ -> ∃-odd′ ⟨ m , help1 m ⟩
∃-odd′ ⟨ m , refl ⟩ -> ∃-even′ ⟨ m , refl ⟩
In the first one, suc m -> m decreases, but refl -> help1 m (on its surface) increases. If you passed refl as the second argument to ∃-odd′, then the termination checker would accept it, since it means the second argument stays the same, while the first one strictly monotonically decreases over a complete chain of two calls.
So how can we change that first recursive call to ∃-odd′ ⟨ m , refl ⟩? By rewriting by sym (help1 m):
∃-even′ ( suc m , refl ) rewrite +-identityʳ m
| +-suc m m
| sym (help1 m)
= even-suc (∃-odd′ (m , refl))
This code is then accepted by the termination checker.
I'm trying to replicate the main lemma in the HoTT book (page 70) for proving the Eckmann Hilton Theorem, only using J (no pattern matching).
It says "But, in general, the two ways of defining horizontal composition agree, α ⋆ β = α ⋆' β, as we can see by induction on α and β and then on the two remaining 1-paths, to reduce everything to reflexivity..."
I'm quite confused as to if the E type signature is correct - should r' and s have different paths? d won't refine, so I assume there's something wrong with E? I also don't really understand which two paths I'm supposed to induct upon to complete the proof, are they r' and s? If so, I don't understand what these final motives should be? Doesn't reducing 'β' down to r eliminate the need for further induction on 1-paths?
Any answers/solutions, and more imporatntly, ways of thinking about the problem are welcome.
_⋆≡⋆'_ : {A : Set} → {a b c : A} {p q : a ≡ b} {r' s : b ≡ c} (α : p ≡ q) (β : r' ≡ s) → (α ⋆ β) ≡ (α ⋆' β)
_⋆≡⋆'_ {A} {a} {b} {c} {p} {q} {r'} {s} α β = J D d p q α c r' s β
where
D : (p q : a ≡ b) → p ≡ q → Set
D p q α = (c : A) (r' s : b ≡ c) (β : r' ≡ s) → (α ⋆ β) ≡ (α ⋆' β)
E : (r' s : b ≡ c) → r' ≡ s → Set
-- E p q β = (r ⋆ β) ≡ (r ⋆' β)
E r' s β = (_⋆_ {A} {b = b} {c} {r} {r} {r' = r'} {s = s} r β) ≡ (r ⋆' β)
e : ((s : b ≡ c) → E s s r)
e r = r --this is for testing purposes
d : ((p : a ≡ b) → D p p r)
d p c r' s β = {!J E e !}
Below is the rest of the code to get here.
module q where
data _≡_ {A : Set} (a : A) : A → Set where
r : a ≡ a
infix 20 _≡_
J : {A : Set}
→ (D : (x y : A) → (x ≡ y) → Set)
-- → (d : (a : A) → (D a a r ))
→ ((a : A) → (D a a r ))
→ (x y : A)
→ (p : x ≡ y)
------------------------------------
→ D x y p
J D d x .x r = d x
_∙_ : {A : Set} → {x y : A} → (p : x ≡ y) → {z : A} → (q : y ≡ z) → x ≡ z
_∙_ {A} {x} {y} p {z} q = J D d x y p z q
where
D : (x₁ y₁ : A) → x₁ ≡ y₁ → Set
D x y p = (z : A) → (q : y ≡ z) → x ≡ z
d : (z₁ : A) → D z₁ z₁ r
d = λ v z q → q
infixl 40 _∙_
_⁻¹ : {A : Set} {x y : A} → x ≡ y → y ≡ x
-- _⁻¹ {A = A} {x} {y} p = J2 D d x y p
_⁻¹ {A} {x} {y} p = J D d x y p
where
D : (x y : A) → x ≡ y → Set
D x y p = y ≡ x
d : (a : A) → D a a r
d a = r
infixr 50 _⁻¹
iₗ : {A : Set} {x y : A} (p : x ≡ y) → p ≡ r ∙ p
iₗ {A} {x} {y} p = J D d x y p
where
D : (x y : A) → x ≡ y → Set
D x y p = p ≡ r ∙ p
d : (a : A) → D a a r
d a = r
iᵣ : {A : Set} {x y : A} (p : x ≡ y) → p ≡ p ∙ r
iᵣ {A} {x} {y} p = J D d x y p
where
D : (x y : A) → x ≡ y → Set
D x y p = p ≡ p ∙ r
d : (a : A) → D a a r
d a = r
_∙ᵣ_ : {A : Set} → {b c : A} {a : A} {p q : a ≡ b} (α : p ≡ q) (r' : b ≡ c) → p ∙ r' ≡ q ∙ r'
_∙ᵣ_ {A} {b} {c} {a} {p} {q} α r' = J D d b c r' a α
where
D : (b c : A) → b ≡ c → Set
D b c r' = (a : A) {p q : a ≡ b} (α : p ≡ q) → p ∙ r' ≡ q ∙ r'
d : (a : A) → D a a r
d a a' {p} {q} α = iᵣ p ⁻¹ ∙ α ∙ iᵣ q
-- iᵣ == ruₚ in the book
_∙ₗ_ : {A : Set} → {a b : A} (q : a ≡ b) {c : A} {r' s : b ≡ c} (β : r' ≡ s) → q ∙ r' ≡ q ∙ s
_∙ₗ_ {A} {a} {b} q {c} {r'} {s} β = J D d a b q c β
where
D : (a b : A) → a ≡ b → Set
D a b q = (c : A) {r' s : b ≡ c} (β : r' ≡ s) → q ∙ r' ≡ q ∙ s
d : (a : A) → D a a r
d a a' {r'} {s} β = iₗ r' ⁻¹ ∙ β ∙ iₗ s
_⋆_ : {A : Set} → {a b c : A} {p q : a ≡ b} {r' s : b ≡ c} (α : p ≡ q) (β : r' ≡ s) → p ∙ r' ≡ q ∙ s
_⋆_ {A} {q = q} {r' = r'} α β = (α ∙ᵣ r') ∙ (q ∙ₗ β)
_⋆'_ : {A : Set} → {a b c : A} {p q : a ≡ b} {r' s : b ≡ c} (α : p ≡ q) (β : r' ≡ s) → p ∙ r' ≡ q ∙ s
_⋆'_ {A} {p = p} {s = s} α β = (p ∙ₗ β) ∙ (α ∙ᵣ s)
In formalization, based path induction is far more convenient than the two-sided version. With based J, we essentially rewrite in the goal type the right endpoint of a path to the left one and the path itself to reflexivity. With non-based J, we rewrite both endpoints to a "fresh" opaque variable, hence we lose the "connection" of the left endpoint to other constructions in scope (since the left endpoint may occur in other types in scope).
I haven't looked at the exact issue with your definition, but I note that with based J it's almost trivial.
data _≡_ {A : Set} (a : A) : A → Set where
r : a ≡ a
infix 20 _≡_
J : {A : Set}{x : A}(P : ∀ y → x ≡ y → Set) → P x r → ∀ {y} p → P y p
J {A} {x} P pr r = pr
tr : {A : Set}(P : A → Set){x y : A} → x ≡ y → P x → P y
tr P p px = J (λ y _ → P y) px p
_∙_ : {A : Set} → {x y z : A} → (p : x ≡ y) → (q : y ≡ z) → x ≡ z
_∙_ {A} {x} {y} {z} p q = tr (x ≡_) q p
ap : {A B : Set}(f : A → B){x y : A} → x ≡ y → f x ≡ f y
ap f {x} {y} p = tr (λ y → f x ≡ f y) p r
infixl 40 _∙_
_∙ᵣ_ : {A : Set} → {b c : A} {a : A} {p q : a ≡ b} (α : p ≡ q) (r' : b ≡ c) → p ∙ r' ≡ q ∙ r'
α ∙ᵣ r' = ap (_∙ r') α
_∙ₗ_ : {A : Set} → {a b : A} (q : a ≡ b) {c : A} {r' s : b ≡ c} (β : r' ≡ s) → q ∙ r' ≡ q ∙ s
q ∙ₗ β = ap (q ∙_) β
_⋆_ : {A : Set} → {a b c : A} {p q : a ≡ b} {r' s : b ≡ c} (α : p ≡ q) (β : r' ≡ s) → p ∙ r' ≡ q ∙ s
_⋆_ {q = q} {r'} α β = (α ∙ᵣ r') ∙ (q ∙ₗ β)
_⋆'_ : {A : Set} → {a b c : A} {p q : a ≡ b} {r' s : b ≡ c} (α : p ≡ q) (β : r' ≡ s) → p ∙ r' ≡ q ∙ s
_⋆'_ {A} {p = p} {s = s} α β = (p ∙ₗ β) ∙ (α ∙ᵣ s)
_⋆≡⋆'_ : {A : Set} → {a b c : A} {p q : a ≡ b} {r' s : b ≡ c} (α : p ≡ q) (β : r' ≡ s) → (α ⋆ β) ≡ (α ⋆' β)
_⋆≡⋆'_ {A} {a} {b} {c} {p} {q} {r'} {s} α β =
J (λ s β → (α ⋆ β) ≡ (α ⋆' β))
(J (λ q α → (α ⋆ r) ≡ (α ⋆' r))
r
α) -- induction on α
β -- induction on β
This is in continuation of this question, based on this answer. Using the technique explained by Saizan, and factoring my fromList-toList proof a bit to avoid the problematic recursion, I managed to fill in all but one cases of fromList-toList. I think it's easiest if I just show everything I have:
{-# OPTIONS --cubical #-}
module _ where
open import Cubical.Core.Everything
open import Cubical.Foundations.Everything hiding (assoc)
data FreeMonoid {ℓ} (A : Type ℓ) : Type ℓ where
[_] : A → FreeMonoid A
ε : FreeMonoid A
_·_ : FreeMonoid A → FreeMonoid A → FreeMonoid A
εˡ : ∀ x → ε · x ≡ x
εʳ : ∀ x → x · ε ≡ x
assoc : ∀ x y z → (x · y) · z ≡ x · (y · z)
squash : isSet (FreeMonoid A)
infixr 20 _·_
open import Cubical.Data.List hiding ([_])
module ListVsFreeMonoid {ℓ} {A : Type ℓ} (AIsSet : isSet A) where
listIsSet : isSet (List A)
listIsSet = isOfHLevelList 0 AIsSet
toList : FreeMonoid A → List A
toList [ x ] = x ∷ []
toList ε = []
toList (m₁ · m₂) = toList m₁ ++ toList m₂
toList (εˡ m i) = toList m
toList (εʳ m i) = ++-unit-r (toList m) i
toList (assoc m₁ m₂ m₃ i) = ++-assoc (toList m₁) (toList m₂) (toList m₃) i
toList (squash m₁ m₂ p q i j) = listIsSet (toList m₁) (toList m₂) (cong toList p) (cong toList q) i j
fromList : List A → FreeMonoid A
fromList [] = ε
fromList (x ∷ xs) = [ x ] · fromList xs
toList-fromList : ∀ xs → toList (fromList xs) ≡ xs
toList-fromList [] = refl
toList-fromList (x ∷ xs) = cong (x ∷_) (toList-fromList xs)
fromList-homo : ∀ xs ys → fromList xs · fromList ys ≡ fromList (xs ++ ys)
fromList-homo [] ys = εˡ (fromList ys)
fromList-homo (x ∷ xs) ys = assoc [ x ] (fromList xs) (fromList ys) ∙ cong ([ x ] ·_) (fromList-homo xs ys)
fromList-toList-· : ∀ {m₁ m₂ : FreeMonoid A} → fromList (toList m₁) ≡ m₁ → fromList (toList m₂) ≡ m₂ → fromList (toList (m₁ · m₂)) ≡ m₁ · m₂
fromList-toList-· {m₁} {m₂} p q = sym (fromList-homo (toList m₁) (toList m₂)) ∙ cong₂ _·_ p q
fromList-toList : ∀ m → fromList (toList m) ≡ m
fromList-toList [ x ] = εʳ [ x ]
fromList-toList ε = refl
fromList-toList (m₁ · m₂) = fromList-toList-· (fromList-toList m₁) (fromList-toList m₂)
fromList-toList (εˡ m i) = isSet→isSet' squash
(fromList-toList-· refl (fromList-toList m))
(fromList-toList m)
(λ i → fromList (toList (εˡ m i)))
(λ i → εˡ m i)
i
fromList-toList (εʳ m i) = isSet→isSet' squash
(fromList-toList-· (fromList-toList m) refl)
(fromList-toList m)
((λ i → fromList (toList (εʳ m i))))
(λ i → εʳ m i)
i
fromList-toList (assoc m₁ m₂ m₃ i) = isSet→isSet' squash
(fromList-toList-· (fromList-toList-· (fromList-toList m₁) (fromList-toList m₂)) (fromList-toList m₃))
(fromList-toList-· (fromList-toList m₁) (fromList-toList-· (fromList-toList m₂) (fromList-toList m₃)))
(λ i → fromList (toList (assoc m₁ m₂ m₃ i)))
(λ i → assoc m₁ m₂ m₃ i)
i
fromList-toList (squash x y p q i j) = ?
Sets are groupoids so I thought I can try doing exactly the same in that last case as before, just one dimension higher. But this is where I start failing: for some reason, two of the six faces cannot be constructed using the fact that FreeMonoid is a set. In more concrete terms, in the two missing faces in the code below, if I just try to refine by putting isSet→isSet' squash in the hole (with no more arguments specified), I already get "cannot refine".
Here's my code for the four faces that I managed to fill in:
fromList-toList (squash x y p q i j) = isGroupoid→isGroupoid' (hLevelSuc 2 _ squash)
{fromList (toList x)}
{x}
{fromList (toList y)}
{y}
{fromList (toList (p i))}
{p i}
{fromList (toList (q i))}
{q i}
{λ k → fromList (toList (p k))}
{fromList-toList x}
{fromList-toList y}
{p}
{λ k → fromList (toList (squash x y p q k i))}
{fromList-toList (p i)}
{fromList-toList (q i)}
{λ k → squash x y p q k i}
{λ k → fromList (toList (p (i ∧ k)))}
{λ k → p (i ∧ k)}
{λ k → fromList (toList (q (i ∨ ~ k)))}
{λ k → q (i ∨ ~ k)}
?
f2
f3
?
f5
f6
i
j
where
f2 = isSet→isSet' squash
(fromList-toList x) (fromList-toList (p i))
(λ k → fromList (toList (p (i ∧ k)))) (λ k → p (i ∧ k))
f3 = isSet→isSet' squash
(fromList-toList y) (fromList-toList (q i))
(λ k → fromList (toList (q (i ∨ ~ k)))) (λ k → q (i ∨ ~ k))
f5 = isSet→isSet' squash (fromList-toList x) (fromList-toList y)
(λ k → fromList (toList (p k)))
(λ k → p k)
f6 = isSet→isSet' squash (fromList-toList (p i)) (fromList-toList (q i))
(λ k → fromList (toList (squash x y p q k i)))
(λ k → squash x y p q k i)
The reported types of the two missing faces are:
Square
(λ k → fromList (toList (p (i ∧ k))))
(λ k → fromList (toList (p k)))
(λ k → fromList (toList (squash x y p q k i)))
(λ k → fromList (toList (q (i ∨ ~ k))))
and
Square
(λ k → p (i ∧ k))
p
(λ k → squash x y p q k i)
(λ k → q (i ∨ ~ k))
Of course, I make no claims that the existing four faces are correct.
So I guess my question is either, what are the two missing faces, or alternatively, what are the correct 6 faces?
The six faces are not arbitrary ones between the endpoints, they are given by the type and other clauses of fromList-toList.
To find them out we can use the strategy from the other answer but one dimension higher. First we declare a cube define through conging of fromList-toList:
fromList-toList (squash x y p q i j) = { }0
where
r : Cube ? ? ? ? ? ?
r = cong (cong fromList-toList) (squash x y p q)
We can then ask agda to solve the six ?s by C-c C-s and after a little cleanup we get:
r : Cube (λ i j → fromList (toList (squash x y p q i j)))
(λ i j → fromList-toList x j)
(λ i j → fromList-toList y j)
(λ i j → squash x y p q i j)
(λ i j → fromList-toList (p i) j)
(λ i j → fromList-toList (q i) j)
r = cong (cong fromList-toList) (squash x y p q)
in this case we are able to use those faces directly as there's no problem with recursion.
fromList-toList (squash x y p q i j)
= isGroupoid→isGroupoid' (hLevelSuc 2 _ squash)
(λ i j → fromList (toList (squash x y p q i j)))
(λ i j → fromList-toList x j)
(λ i j → fromList-toList y j)
(λ i j → squash x y p q i j)
(λ i j → fromList-toList (p i) j)
(λ i j → fromList-toList (q i) j)
i j
By the way, if you are going to prove more equalities by induction it may pay off to implement a more general function first:
elimIntoProp : (P : FreeMonoid A → Set) → (∀ x → isProp (P x))
→ (∀ x → P [ x ]) → P ε → (∀ x y → P x → P y → P (x · y)) → ∀ x → P x
as paths in FreeMonoid A are a proposition.
Suppose I have, using the cubical-demo library, the following things in scope:
i : I
p0 : x ≡ y
p1 : x' ≡ y'
q0 : x ≡ x'
q1 : y ≡ y'
How do I then construct
q' : p0 i ≡ p1 i
?
One way is by contracting singleton pairs with J, there might be simpler proofs though.
open import Cubical.PathPrelude
q' : ∀ {A : Set} (i : I) (x : A)
x' (q0 : x ≡ x')
y (p0 : x ≡ y)
y' (p1 : x' ≡ y')
(q1 : y ≡ y') → p0 i ≡ p1 i
q' i x = pathJ _ (pathJ _ (pathJ _ (\ q1 → q1)))
Another one I've come up with is I think closer to the spirit of the original problem instead of going around:
slidingLid : ∀ (p₀ : a ≡ b) (p₁ : c ≡ d) (q : a ≡ c) → ∀ i → p₀ i ≡ p₁ i
slidingLid p₀ p₁ q i j = comp (λ _ → A)
(λ{ k (i = i0) → q j
; k (j = i0) → p₀ (i ∧ k)
; k (j = i1) → p₁ (i ∧ k)
})
(inc (q j))
This one has the very nice property that it degenerates to q at i = i0 definitionally:
slidingLid₀ : ∀ p₀ p₁ q → slidingLid p₀ p₁ q i0 ≡ q
slidingLid₀ p₀ p₁ q = refl
I've found another solution to this, which is more explicit that it is gluing together a prefix of p0 (flipped), q0, and a prefix of p1:
open import Cubical.PathPrelude
module _ {ℓ} {A : Set ℓ} where
midPath : ∀ {a b c d : A} (p₀ : a ≡ b) (p₁ : c ≡ d) → (a ≡ c) → ∀ i → p₀ i ≡ p₁ i
midPath {a = a} {c = c} p₀ p₁ q i = begin
p₀ i ≡⟨ transp (λ j → p₀ (i ∧ j) ≡ a) refl ⟩
a ≡⟨ q ⟩
c ≡⟨ transp (λ j → c ≡ p₁ (i ∧ j)) refl ⟩
p₁ i ∎
I have the following definition
open import Relation.Binary.PropositionalEquality
data _≅_ (A B : Set) : Set where
mkBij : (f : A → B) (g : B → A)
→ (∀ a → a ≡ g (f a))
→ (∀ b → b ≡ f (g b))
→ A ≅ B
And I'm trying to show transitivity. I have what I need, but I don't know how to combine them to get the proof object I want. This is the proof so far.
transtv : ∀ {A B C} → A ≅ B → B ≅ C → A ≅ C
transtv (mkBij f₁ g₁ x y) (mkBij f₂ g₂ w z) =
mkBij (λ x₁ → f₂ (f₁ x₁)) (λ z₁ → g₁ (g₂ z₁))
(λ a → let xa = x a
wb = w (f₁ a)
in {!!})
(λ c → let zc = z c
yb = y (g₂ c)
in {!!})
In the first hole, I have these: (the second hole is identical)
Goal: a ≡ g₁ (g₂ (f₂ (f₁ a)))
wb : f₁ a ≡ g₂ (f₂ (f₁ a))
xa : a ≡ g₁ (f₁ a)
Now, it's obvious that if I replace f₁ a with g₂ (f₂ (f₁ a)) in xa I get to the goal. But I don't know how to do this substitution in agda. What kind of function or language construct do I need to do this?
You can write it very compactly as
trans xa (cong g₁ wb)
Or, using Function._⟨_⟩_:
xa ⟨ trans ⟩ (cong g₁ wb)
I solved it with equational reasoning in the following way:
transtv : ∀ {A B C} → A ≅ B → B ≅ C → A ≅ C
transtv (mkBij f₁ g₁ x y) (mkBij f₂ g₂ w z) =
mkBij (λ x₁ → f₂ (f₁ x₁)) (λ z₁ → g₁ (g₂ z₁))
(λ a → let xa = x a
wb = w (f₁ a)
in begin
a
≡⟨ xa ⟩
g₁ (f₁ a)
≡⟨ cong g₁ wb ⟩
g₁ (g₂ (f₂ (f₁ a)))
∎)
(λ c → let zc = z c
yb = y (g₂ c)
in begin
c
≡⟨ zc ⟩
f₂ (g₂ c)
≡⟨ cong f₂ yb ⟩
f₂ (f₁ (g₁ (g₂ c)))
∎)