Subtypes
This chapter explores the definition and usage of subtypes in Lean. We will introduce their basic properties, discuss common operations, and demonstrate their application in mathematical reasoning and program verification.
A subtype is a way to define a restricted entity of a given type by specifying a condition that the elements of this type must satisfy. A subtype of a type A
is typically defined using a predicate P : A → Prop
, which assigns a proposition to each element of A
. The corresponding subtype, denoted as Subtype P
or {a : A // P a}
, consists of all elements a : A
that satisfy P
.
Subtype.lean
Subtypes play a crucial role in formal verification, as they allow us to encode mathematical objects with additional properties. For example, we can define the subtype of even natural numbers, the positive real numbers, or the set of invertible matrices. By doing so, we ensure that any element of the subtype inherently satisfies the given condition, reducing the need for repetitive proof obligations.
If we #print Subtype
, Lean returns
structure Subtype.{u} : {α : Sort u} → (α → Prop) → Sort (max 1 u)
number of parameters: 2
constructor:
Subtype.mk : {α : Sort u} → {p : α → Prop} → (val : α) → p val → Subtype p
fields:
val : α
property : p self.val
The Subtype
structure in Lean is parameterized by a type α
and a predicate p : α → Prop
, which defines adscription to the subtype. It includes a constructor, Subtype.mk
, that takes a value val : α
along with a proof of p val
, ensuring that val
satisfies the predicate. An instance of Subtype p
has two fields: val
, which holds the underlying value, and property
, which provides the proof that val
satisfies p
.
Examples of subtypes
The False subtype
Given a type A
, we can consider the False
subtype on A
.
The True subtype
Given a type A
, we can consider the True
subtype on A
.
The image of a function
We introduce the image of a function. Given two types A
and B
and a function f : A → B
, we define the image of f
, denoted as Im f
, as the subtype of B
consisting of all elements that are mapped from some a : A
under f
.
Elements of a subtype
To create an element of Subtype P
, we use the Subtype.mk
function, which maps an element a : A
and a proof h : P a
to an element of type Subtype P
.
Two elements of type Subtype P
are equal if and only if their corresponding values in A
are also equal. For this we have the the theorems Subtype.eq
and Subtype.eq_iff
.
The inclusion function
The inclusion function is a function of a subtype into its underlying type: it simply extracts the value of a Subtype P
element, discarding its proof.
Thanks to Subtype.eq
we can prove that the inclusion function is always injective.
Functions and Subtypes
Restriction
We can formalize the notion of restricting functions to subtypes. Any function f : A → B
can be restricted to a subtype by applying f
only to the underlying values of the subtype elements. Restriction provides a way to transform elements of type A → B
into elements of type Subtype P → B
.
Correstriction
Given a function f : A → B
and a predicate Q
on B
, we can correstrict f
to Subtype P
, provided that every b : Im f
satisfies Q
. If the above condition holds, correstriction provides a way to transform elements of type A → B
into elements of type A → Subtype Q
.
Birrestriction
Given a function f : A → B
, a predicate P
on A
and a predicate Q
on B
, we can birrestrict f
to the respective subtypes, provided that f a
satisfies Q
for every a : Subtype P
. If the above condition holds, birrestriction provides a way to transform elements of type A → B
into elements of type Subtype P → Subtype Q
.
Birrestriction.lean
In particular, given two predicates P1
and P2
on a type A
, the following function establishes a transformation from the subtype corresponding to P1
to the subtype corresponding to P2
, provided that P1
implies P2
. This is achieved by birrestricting the identity function.
Equalizers
In this section, we introduce the concept of the equalizer of two functions, a construction that identifies the subtype of a domain where the two functions agree. Beyond its definition as a subtype, the equalizer is also characterized by a universal property: it serves as the most general type equipped with a map into A
on which the functions agree.
Given two functions f, g : A → B
, the equalizer of f
and g
is the subtype of A
consisting on all elements a : A
such that f a = g a
.
It commes equipped with the inclusion function, from the equalizer to A
.
This inclusion satisfies that f ∘ (incEq f g) = g ∘ (incEq f g)
.
Equalizer.lean
Universal property of the equalizer
The universal property of the equalizer characterizes it not merely as a subtype, but as a universal solution to the problem of mediating between f
and g
. The universal property states that the pair (Eq, inc)
is initial among all pairs (C, h)
, where C
is a type and h : C → A
is a function satisfying f ∘ h = g ∘ h
.
That is, if C
is a type and h : C → A
is a function satisfying f ∘ h = g ∘ h
, then there exists a unique function u : C → Eq
such that inc ∘ u = h
.
Equalizer.lean
-- If there is another function `h : C → A` satisfying
-- `f ∘ h = g ∘ h`, then there exists a function `u : C → Eq f g`
def u {A B C : Type} {f g : A → B} {h : C → A} (h1 : f ∘ h = g ∘ h) : C → Eq f g := by
intro c
exact Subtype.mk (h c) (congrFun h1 c)
-- The function `u` satisfies that `incEq f g ∘ u = h`
theorem TEqIncEq {A B C : Type} {f g : A → B} {h : C → A} (h1 : f ∘ h = g ∘ h) :
(incEq f g) ∘ (u h1) = h := by
apply funext
intro c
exact rfl
-- The function `u` is unique in the sense that, if there is another function
-- `v : C → Eq f g` satisfying `incEq f g ∘ v = h`, then `v = u`.
theorem TEqUni {A B C : Type} {f g : A → B} {h : C → A} (h1 : f ∘ h = g ∘ h)
(v : C → Eq f g) (h2 : (incEq f g) ∘ v = h) : v = u h1 := by
apply funext
intro c
apply Subtype.eq
calc
(v c).val = ((incEq f g) ∘ v) c := rfl
_ = h c := congrFun h2 c
_ = (u h1 c).val := rfl
In other words, any function into A
that “equalizes” f
and g
factors uniquely through the equalizer. This property ensures that the equalizer is the most general and canonical way to capture the elements where two functions agree.
Exercises
Subtypes
Restriction
Correstriction
Theorems TUPCorrest
and TUPCorrestUn
establish the universal property of the correstriction of a function. The first result, TUPCorrest
, states that for any function f : A → B
that respects a predicate Q
on B
(i.e., Q b
holds for all b : Im f
)), the function f
can be expressed as the composition of the inclusion function inc
and its correstriction correst f h
, that is f = inc ∘ (correst f h)
. The second result, TUPCorrestUn
, establishes the uniqueness of the correstriction. If there exists another function g : A → Subtype P
such that f = inc ∘ g
, then g
must be exactly correst f h
.
Exercises.lean
-- f = inc ∘ correst
theorem TUPCorrest {A B : Type} {Q : B → Prop} {f : A → B}
(h : ∀ (b : Im f), Q b.val) : f = (@inc B Q) ∘ (correst f h) := by sorry
-- Unicity
theorem TUPCorrestUn {A B : Type} {Q : B → Prop} {f : A → B}
(h : ∀ (b : Im f), Q b.val) (g : A → Subtype Q) (h1 : f = (@inc B Q) ∘ g)
: (correst f h) = g := by sorry