Zulip Chat Archive
Stream: new members
Topic: Defining simple categories
Vlad Tsyrklevich (Aug 25 2025 at 13:58):
I'm reading Category Theory in Context and I decided to solve a simple exercise using Lean/mathlib just to explore how category theory is implemented in mathlib. The exercise itself was fairly simple, but I spent quite a lot of time defining two categories and a functor between them. In my case, I define one (thin) category with 4 objects and 3 (non-identity) morphisms and then another category with 3 objects and 3 morphisms. I imagine there is probably an easier way to define categories than what I did and I am just not aware of the right tools to do so? Pointers appreciated.
-- Exercise 1.6.iv. Find an example to show that a faithful functor need not preserve epimorphisms.
-- Argue by duality, or by another counterexample, that a faithful functor need not preserve monomorphisms.
/--
Define a category with the following morphisms:
a → b → c
|
\ → d
-/
inductive t₁ | a | b | c | d
instance Cat_t₁ : SmallCategory t₁ where
Hom X Y := PLift (X = t₁.a ∨ (X = t₁.b ∧ Y = t₁.c) ∨ (X = t₁.b ∧ Y = t₁.d) ∨ (X = Y))
id X := by
simp
exact { down := trivial }
comp X Y := by
refine PLift.pure ?_
rcases PLift.down X with _ | _ | _ | _ <;> rcases PLift.down Y with _ | _ | _ | _ <;> simp_all
/--
Define a category with the following morphisms:
A → B ⇉ C (but there is only one morphism A → C)
-/
inductive t₂ | A | B | C
instance Cat_t₂ : SmallCategory t₂ where
Hom
| t₂.A, t₂.A => PUnit
| t₂.B, t₂.B => PUnit
| t₂.C, t₂.C => PUnit
| t₂.A, t₂.B => PUnit
| t₂.A, t₂.C => PUnit
| t₂.B, t₂.C => Sum PUnit PUnit -- inl ⋆ and inr ⋆ are different arrows
| _, _ => PEmpty
id X := by
rcases X <;> simp <;> exact PUnit.unit
comp {x y z} X Y := by
rcases x <;> rcases y <;> rcases z <;> simp
-- kinda ugly but correct
all_goals first | exact PUnit.unit | exact X | exact Y | exact Sum.inl PUnit.unit
-- Manually prove these because aesop times out.
id_comp {X Y} (f) := by rcases X <;> rcases Y <;> simp
comp_id {X Y} (f) := by rcases X <;> rcases Y <;> simp
assoc {W X Y Z} (f g h) := by
simp
rcases W <;> rcases X <;> rcases Y <;> rcases Z
all_goals (simp at f g h; aesop)
def t₁_t₂_functor : Functor t₁ t₂ := {
obj X :=
match X with
| t₁.a => t₂.A
| t₁.b => t₂.B
| t₁.c => t₂.C
| t₁.d => t₂.C
map {x y} X := by
match x, y with
| t₁.a, t₁.a => exact PUnit.unit
| t₁.b, t₁.b => exact PUnit.unit
| t₁.c, t₁.c => exact PUnit.unit
| t₁.d, t₁.d => exact PUnit.unit
| t₁.a, t₁.b => exact PUnit.unit
| t₁.b, t₁.c => exact Sum.inl PUnit.unit
| t₁.a, t₁.c => exact PUnit.unit
| t₁.b, t₁.d => exact Sum.inr PUnit.unit
| t₁.a, t₁.d => exact PUnit.unit
| t₁.d, t₁.c => have := PLift.down X; simp_all
| t₁.d, t₁.b => have := PLift.down X; simp_all
| t₁.d, t₁.a => have := PLift.down X; simp_all
| t₁.c, t₁.d => have := PLift.down X; simp_all
| t₁.c, t₁.b => have := PLift.down X; simp_all
| t₁.c, t₁.a => have := PLift.down X; simp_all
| t₁.b, t₁.a => have := PLift.down X; simp_all
/-
repeat' split
all_goals try exact PUnit.unit
all_goals try (have := PLift.down X; simp_all)
· exact Sum.inl PUnit.unit
· exact Sum.inr PUnit.unit
-/
-- Manually prove because aesop times out.
map_comp {x y z} X Y := by
simp
rcases x <;> rcases y <;> rcases z <;> simp <;> try rfl
all_goals
· have := PLift.down X;
have := PLift.down Y;
simp_all
}
theorem exercise_1_6_iv_part1 :
∃ α β : Type,
∃ _ : CategoryTheory.SmallCategory α,
∃ _ : CategoryTheory.SmallCategory β,
∃ F : CategoryTheory.Functor α β,
∃ X Y : α,
∃ f : X ⟶ Y,
F.Faithful ∧ CategoryTheory.Epi f ∧ ¬CategoryTheory.Epi (F.map f) := by
refine ⟨t₁, t₂, Cat_t₁, Cat_t₂, t₁_t₂_functor, t₁.a, t₁.b, PLift.up (Or.inl rfl), ?_⟩
refine ⟨?_, ⟨?_, ?_⟩⟩
· refine { map_injective := fun {X Y} ↦ ?_ }
simp [t₁_t₂_functor]
rcases X <;> rcases Y <;> simp [Function.Injective]
all_goals exact fun ⦃a₁ a₂⦄ => eq_of_comp_right_eq fun {X} => congrFun rfl
· exact { left_cancellation := fun {z} g h d ↦ eq_of_comp_right_eq fun {X} => congrFun rfl }
· by_contra h'
have := h'.left_cancellation (Z := t₂.C) (Sum.inl PUnit.unit : t₂.B ⟶ t₂.C) (Sum.inr PUnit.unit : t₂.B ⟶ t₂.C)
simp [t₁_t₂_functor, Cat_t₂] at this
theorem exercise_1_6_iv_part2 :
∃ α β : Type,
∃ _ : CategoryTheory.SmallCategory αᵒᵖ,
∃ _ : CategoryTheory.SmallCategory βᵒᵖ,
∃ F : CategoryTheory.Functor αᵒᵖ βᵒᵖ,
∃ X Y : αᵒᵖ,
∃ f : X ⟶ Y,
F.Faithful ∧ CategoryTheory.Mono f ∧ ¬CategoryTheory.Mono (F.map f) := by
obtain ⟨s₁, s₂, cat₁, cat₂, functor, x, y, f, ⟨h₁, ⟨h₂, h₃⟩⟩⟩ := exercise_1_6_iv_part1
refine ⟨s₁, s₂, cat₁.opposite, cat₂.opposite, functor.op, Opposite.op y, Opposite.op x, f.op, ?_⟩
refine ⟨Functor.instFaithfulOppositeOp, ⟨op_mono_of_epi f, ?_⟩⟩
· contrapose! h₃
simp only [Functor.op_obj, Functor.op_map, Quiver.Hom.unop_op] at h₃
have := unop_epi_of_mono (functor.map f).op
simpa using this
Junyan Xu (Aug 25 2025 at 14:17):
I'm not sure if aesop works better this way, but the homs in such categories are usually defined like this:
open t₂ in
inductive t₂hom : t₂ → t₂ → Type
| id (x : t₂) : t₂hom x x
| AB : t₂hom A B
| AC : t₂hom A C
| BC₁ : t₂hom B C
| BC₂ : t₂hom B C
see e.g. CategoryTheory.Limits.WalkingParallelPairHom
Vlad Tsyrklevich (Aug 25 2025 at 15:29):
Thanks for the pointer. I refactored using your definition, and it is a bit cleaner, thoughaesop still fails to fill in all of the same definitions.
I'm a little surprised there isn't a macro of some sort to specify simple categories/diagrams, though I'm a beginner and have no idea its usefulness.
Kenny Lau (Aug 26 2025 at 19:34):
Vlad Tsyrklevich said:
there isn't a macro of some sort to specify simple categories/diagrams
it's fin_cases after you provide the corresponding Fintype instances
Vlad Tsyrklevich (Aug 26 2025 at 19:38):
Not sure I follow, how would one define one of the categories I defined earlier using this?
Kenny Lau (Aug 26 2025 at 21:03):
Vlad Tsyrklevich said:
Not sure I follow, how would one define one of the categories I defined earlier using this?
you don't change the definitions, but you provide an instance of Fintype t₁
Kenny Lau (Aug 26 2025 at 21:03):
and also Fintype the homs
Last updated: Dec 20 2025 at 21:32 UTC