Quotients
In mathematics, we often want to consider objects modulo some form of identification—treating different representatives as essentially the same. This practice leads us naturally to the notion of equivalence relations, which formally define when two elements should be considered indistinguishable for our purposes.
Once we have an equivalence relation, we can group elements into equivalence classes—collections of elements that are all equivalent to each other. The process of forming a new structure out of these equivalence classes is called taking a quotient. Quotients allow us to construct new types that “forget” unnecessary distinctions while preserving the structure we care about.
This chapter introduces the foundations of working with quotients in a formal setting. We begin by reviewing equivalence relations and exploring concrete examples. We then move to the notion of setoids, which package a type together with an equivalence relation—an essential concept in Lean.
Next, we delve into the construction of quotients themselves. We examine how to reason about their elements, understand the projection function from a type to its quotient, and work through illustrative examples.
Finally, we explore how functions behave in the presence of quotient types. Specifically, we study how functions defined on a type can be astricted, coastricted, and biastricted. These ideas are key when reasoning about quotient structures in a type-theoretic setting.
The chapter concludes with exercises to reinforce our understanding and help us apply these concepts.
Equivalence relations
A relation R : A → A → Prop
is an equivalence relation if it is Reflexive
, Symmetric
and Transitive
. This is already implemented as Equivalence
.
If we #print Equivalence
, Lean returns:
structure Equivalence.{u} : {α : Sort u} → (α → α → Prop) → Prop
number of parameters: 2
constructor:
Equivalence.mk : ∀ {α : Sort u} {r : α → α → Prop},
(∀ (x : α), r x x) → (∀ {x y : α}, r x y → r y x) → (∀ {x y z : α}, r x y → r y z → r x z) → Equivalence r
fields:
refl : ∀ (x : α), r x x
symm : ∀ {x y : α}, r x y → r y x
trans : ∀ {x y z : α}, r x y → r y z → r x z
This structure takes two parameters: a type α : Sort u
and a binary relation r : α → α → Prop
. It encapsulates the three fundamental properties that define an equivalence relation: reflexivity, symmetry, and transitivity. The Equivalence.mk
constructor allows us to create an instance of Equivalence r
by providing proofs for these three properties. Specifically, the refl
field ensures that every element is related to itself ∀ (x : α), r x x
, the symm
field guarantees that the relation is symmetric ∀ {x y : α}, r x y → r y x
, and the trans
field enforces transitivity ∀ {x y z : α}, r x y → r y z → r x z
.
Examples of equivalence relations
The diagonal relation
Given a type A
, the diagonal relation on A
is an equivalence relation on A
.
The total relation
Given a type A
, the total relation on A
is an equivalence relation on A
.
The kernel of a function
We introduce the kernel of a function and demonstrate that it defines an equivalence relation. Given two types A
and B
and a function f : A → B
, we define the kernel of f
, denoted as Ker f
, as a binary relation on A
where two elements a1, a2 : A
are related if and only if they have the same image under f
, i.e., f a1 = f a2
. We then establish that Ker f
satisfies the properties of an equivalence relation.
Kernel.lean
def Ker {A B : Type} (f : A → B) : A → A → Prop := by
intro a1 a2
exact f a1 = f a2
-- The kernel of a function is an equivalence relation
theorem TKerEqv {A B : Type} {f : A → B} : Equivalence (Ker f) := {
refl := by
intro a
exact rfl
symm := by
intro a1 a2 h1
exact h1.symm
trans := by
intro a1 a2 a3 h1 h2
exact h1.trans h2
}
Setoids
A Setoid is a type class that encapsulates an equivalence relation on a given type.
If we #print Setoid
, Lean returns
class Setoid.{u} : Sort u → Sort (max 1 u)
number of parameters: 1
constructor:
Setoid.mk : {α : Sort u} → (r : α → α → Prop) → Equivalence r → Setoid α
fields:
r : α → α → Prop
iseqv : Equivalence Setoid.r
Given a type α
, a Setoid α
consists of a binary relation r : α → α → Prop
and a proof that r
is an equivalence relation. The constructor Setoid.mk
allows defining such structures by providing both the relation and its proof of equivalence. An element of type A Setoid α
has two fields, r : α → α → Prop
the equivalence relation on α
and iseqv : Equivalence Setoid.r
, a proof that r
is an equivalence relation.
Examples of setoids
Since both the diagonal relation and the total relation are equivalence relations, we can define their corresponding setoids. Additionally, we can construct the setoid associated with the kernel of a function.
The diagonal setoid
The total setoid
The kernel setoid
Quotients
The significance of Setoid
lies in its role in quotienting, where elements of α
can be grouped into equivalence classes based on r
, leading to the Quotient
construction. The Quotient
type provides a structured way to form quotient types based on equivalence relations.
If we #print Quotient
, Lean returns
def Quotient.{u} : {α : Sort u} → Setoid α → Sort u :=
fun {α} s => Quot Setoid.r
The Quotient
type constructs the quotient of a given setoid, encapsulating an equivalence relation on a type. It takes an implicit type α
along with an instance of Setoid α
, which defines an equivalence relation r
on α
. By applying Quot Setoid.r
, it forms a new type in which elements of α
are identified according to r
. The resulting type remains in the same universe level Sort u
, preserving the type hierarchy of α
. Unlike Quot
, which can be defined for arbitrary binary relations, Quotient
is specifically tailored for equivalence relations, ensuring a structured and well-behaved quotienting mechanism in Lean.
Note that for a type A
and an element S
of type Setoid A
m, the term Quotient S
is a type.
Examples of quotients
With the setoids defined above, we can construct the quotient of a type A
by its diagonal relation, the quotient of A
by the total relation and the quotient of A
by the kernel of a function.
The diagonal quotient
The total quotient
The kernel quotient
Elements of a quotient
To create an element of Quotient S
, we use the Quotient.mk S a
function, which maps an element a : A
to its equivalence class, that is, the elements of type A
that are related with a
.
Two equivalence classes are equal if and only if their representatives are related by the underlying equivalence relation. First, we use Quotient.exact
, which states that if two classes are equal, then their representatives must be related. Conversely, we apply Quotient.sound
, which ensures that if two elements are related, then their classes are equal. This result confirms that quotient types faithfully represent equivalence classes, ensuring that equality in the quotient type corresponds precisely to the equivalence relation in the original type.
The projection function
The projection function is a function of a type into its quotient: it simply constructs the class of a given element.
Projection.lean
An essential property of quotient types is that every element q
of type Quotient S
has a representative in A
, meaning there exists some a : A
such that Quotient.mk S a
produces the element q
. This property is formalized by Quotient.exists_rep
. This proposition implies, in particular, that the projection function is always surjective.
Functions and Quotient types
Astriction
We can formalize the notion of astricting functions to quotients. Any function f : A → B
can be astricted to a quotient by a setoid S
on B
by considering classes on the images under f
. Astriction provides a way to transform elements of type A → B
into elements of type A → Quotient S
.
Coastriction
Given a function f : A → B
and a setoid R
on A
, we can coastrict f
to Quotient R
, provided that every pair related according to the underlying relation of R
is also related according to Ker f
. For this we will apply the keyword Quotient.lift
. If the above condition holds, coastriction provides a way to transform elements of type A → B
into elements of type Quotient R → B
.
Biastriction
Given a function f : A → B
, a setoid R
on A
and a setoid S
on B
, we can biastrict f
to the respective quotient types, provided that if a pair a1 a2 : A
is related according to the underlying relation of R
then the pair (f a1), (f a2) : B
is related according to the underlying relation of S
. If the above condition holds, biastriction provides a way to transform elements of type A → B
into elements of type Quotient R → Quotient S
.
Biastriction.lean
In particular, given two setoids R1
and R2
on a type A
, the following function establishes a transformation from the type Quotient R1
to the type Quotient R2
, provided that the underlying relation of R1
implies the underlying relation of R2
. This is achieved by biastricting the identity function.
Exercises
Prove that the meet of two equivalence relations is an equivalence relation.
Exercises.lean
If two setoids have equivalent underlying relations, the corresponding quotient types are equal.
Exercises.lean
Prove the The Universal Property of Quotient types, which states that the Kernel of the projection function is precisely the original relation of the setoid.
Exercises.lean
The astriction ast f
is equal to pr ∘ f
.
Exercises.lean
Theorems TUPCoast
and TUPCoasttUn
establish the universal property of the coastriction of a function. The first result, TUPCoast
, states that for any function f : A → B
that respects a setoid relation R
on A
(i.e., for every pair a1 a2 : A
, if R.r a1 a2
then f a1 = f a2
)), the function f
can be expressed as the composition of its coastriction coast f h
with the projection function pr
, that is f = coast ∘ pr
. The second result, TUPCoastUn
, establishes the uniqueness of the coastriction. If there exists another function g : Quotient R → B
such that f = g ∘ pr
, then g
must be exactly coast f h
.
Exercises.lean
-- f = coast ∘ pr
theorem TUPCoast {A B : Type} {R : Setoid A} {f : A → B} (h : ∀ (a1 a2 : A),
R.r a1 a2 → (Ker f) a1 a2) : f = (coast f h) ∘ (@pr A R) := by sorry
-- Unicity
theorem TUPCoastUn {A B : Type} {R : Setoid A} {f : A → B} (h : ∀ (a1 a2 : A),
R.r a1 a2 → (Ker f) a1 a2) (g : Quotient R → B) (h1 : f = g ∘ (@pr A R)) :
(coast f h) = g := by sorry
We next introduce the concept of isomorphic types and establish that, under Classical.choice
, isomorphism defines an equivalence relation. We first define Iso A B
as the subtype of all functions from A
to B
that are isomorphisms, meaning they admit a two-sided inverse. Two types A
and B
are then said to be isomorphic, written A ≅ B
, if there exists at least one such isomorphism, formalized as the proposition Nonempty (Iso A B)
. Prove that being isomorphic is an equivalence relation on Type
.
Exercises.lean
-- The subtype of all isomorphisms from a type `A` to a type `B`
def Iso (A B : Type) := {f : A → B // isomorphism f}
-- Two types A and B are isomorphic if there is some isomorphism from `A` to `B`
def Isomorphic : Type → Type → Prop := by
intro A B
exact Nonempty (Iso A B)
-- Notation for Isomorphic types (`\cong`)
notation : 65 lhs:65 " ≅ " rhs:66 => Isomorphic lhs rhs
-- Being isomorphic is an equivalence relation
theorem TIsoEqv : Equivalence Isomorphic := by sorry
Assuming Classical.choice
, every type A
is isomorphic to the quotient of A
by the diagonal relation, A / diag
.
Under Classical.choice
, any two Nonempty
types A
and B
have isomorphic quotients A / Total
and B / Total
.
Exercises.lean
Assuming Classical.choice
prove that, for every function f : A → B
, the quotient A / Ker f
is isomorphic to Im f
.