Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(category_theory): basic definitions #152

Merged
merged 40 commits into from
Aug 7, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
933f4a0
feat(category_theory): basic definitions
Jun 4, 2018
46e694c
fixing formatting in documentation
Jun 4, 2018
88dd1f0
corrections from review
semorrison Jun 4, 2018
4341b14
removing second ematch attribute on associativity_lemma
semorrison Jun 4, 2018
26dde34
being slightly more careful about variables
semorrison Jun 4, 2018
062d55f
fix(notation): transformation components
semorrison Jun 15, 2018
456594a
fix(notation): more conventional notation for composition
semorrison Jun 15, 2018
eb4dc51
Merge branch 'master' into category_theory
semorrison Jun 20, 2018
efe51ee
adjusting namespaces, capitalisation, and headers
semorrison Jul 2, 2018
fff0238
move functor/default.lean to functor.lean
Jul 27, 2018
ce7194d
oops
Jul 27, 2018
c46e78b
fixing indentation
Jul 27, 2018
0205498
namespace for instances
Jul 27, 2018
e2d0ca4
removing unnecessary `@>` notation for natural transformations
Jul 27, 2018
a949270
Merge commit 'a7b749f0a743ab467e1cb7d688e4a8a0f7a82874' into category…
semorrison Aug 3, 2018
10d77a8
renaming, namespacing, and removing a spurious attribute
Aug 6, 2018
7d3cadd
better naming, namespaces, and coercions
Aug 6, 2018
176a12f
updating documentation
Aug 6, 2018
3eb8c2a
Merge branch 'category_theory' of github.com:semorrison/mathlib into …
semorrison Aug 6, 2018
0e3229d
Merge branch 'master' into category_theory
semorrison Aug 6, 2018
3a49fea
renaming id
Aug 7, 2018
14117a0
reordering definitions
Aug 7, 2018
c030b8d
Merge branch 'category_theory' of github.com:semorrison/mathlib into …
Aug 7, 2018
2b4621d
rfl lemmas for has_one
Aug 7, 2018
433aae7
formatting
Aug 7, 2018
52ed9b7
formatting
Aug 7, 2018
ac4b311
renaming: snake_case
Aug 7, 2018
c3984f0
renaming coe rfl lemmas
Aug 7, 2018
b68bc6e
functoriality -> map_comp
Aug 7, 2018
8f59e1a
rfl lemmas for identity C and identity F (reducing both to `1`)
Aug 7, 2018
d23e348
renaming ext lemma to `ext`
Aug 7, 2018
e29fda0
rename `natural_transformation` to `nat_trans`
Aug 7, 2018
b832f05
rename `make_lemma` to `restate_axiom`
Aug 7, 2018
cb93d51
renaming nat_trans.components to nat_trans.app
Aug 7, 2018
eaac4c0
oops, fix import
Aug 7, 2018
1ce8b0a
adding doc_comments, and `protected`
Aug 7, 2018
14ba452
formatting
Aug 7, 2018
18db512
removing `has_one` instances, per zulip chat, and adding a `vcomp.ass…
Aug 7, 2018
3aa69cb
removing the attribute that `restate_axiom` used to add
semorrison Aug 7, 2018
569241e
fixing names
semorrison Aug 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 43 additions & 0 deletions category_theory/category.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
-- Copyright (c) 2017 Scott Morrison. All rights reserved.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header we use look like:

/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
notation, basic datatypes and type classes
-/

so use /--/ instead of --. The difference looks minor, but might be helpful when we use scripts to analyze the header.

-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Stephen Morgan, Scott Morrison

import tactic.make_lemma
import tactic.interactive

namespace categories
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about category_theory instead?


universes u v

meta def obviously := `[skip]

/-
The propositional fields of `category` are annotated with the auto_param `obviously`, which is just a synonym for `skip`.
Actually, there is a tactic called `obviously` which is not part of this pull request, which should be used here. It successfully
discharges a great many of these goals. For now, proofs which could be provided entirely by `obviously` (and hence omitted entirely
and discharged by an auto_param), are all marked with a comment "-- obviously says:".
-/

class category (Obj : Type u) : Type (max u (v+1)) :=
(Hom : Obj → Obj → Type v)
(identity : Π X : Obj, Hom X X)
(compose : Π {X Y Z : Obj}, Hom X Y → Hom Y Z → Hom X Z)
(left_identity : ∀ {X Y : Obj} (f : Hom X Y), compose (identity X) f = f . obviously)
(right_identity : ∀ {X Y : Obj} (f : Hom X Y), compose f (identity Y) = f . obviously)
(associativity : ∀ {W X Y Z : Obj} (f : Hom W X) (g : Hom X Y) (h : Hom Y Z), compose (compose f g) h = compose f (compose g h) . obviously)

notation `𝟙` := category.identity -- type as \b1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can imagine that we will also love to have this notation for the unit object in a monoidal category. But then... we also want good notation for identity morphisms, and I don't have a good alternative in mind.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the double-struck 1, there are several other variant numeral 1s available in unicode:

"bold": 𝟏
"sans-serif": 𝟣
"sans-serif bold": 𝟭
"monospace": 𝟷

Currently none of these have shortcuts in VSCode, but those are easy to add. My monoidal categories library hasn't fixed on any notation for monoidal units. I'm not sure that having two slightly different 1s is a good idea.

infixr ` ≫ `:80 := category.compose -- type as \gg
infixr ` ⟶ `:10 := category.Hom -- type as \h

-- make_lemma is a command that creates a lemma from a structure field, discarding all auto_param wrappers from the type.
make_lemma category.left_identity
make_lemma category.right_identity
make_lemma category.associativity
-- We tag some lemmas with the attribute `@[ematch]`, for later automation. (I'd be happy to change this to e.g. `@[search]`.)
attribute [simp,ematch] category.left_identity_lemma category.right_identity_lemma category.associativity_lemma

abbreviation large_category (C : Type (u+1)) : Type (u+1) := category.{u+1 u} C
abbreviation small_category (C : Type u) : Type (u+1) := category.{u u} C

end categories
75 changes: 75 additions & 0 deletions category_theory/functor/default.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
-- Copyright (c) 2017 Scott Morrison. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Tim Baumann, Stephen Morgan, Scott Morrison

import ..category

open categories

namespace categories.functor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think everything should went into category_theory.


universes u₁ v₁ u₂ v₂ u₃ v₃

structure Functor (C : Type u₁) [category.{u₁ v₁} C] (D : Type u₂) [category.{u₂ v₂} D] : Type (max u₁ v₁ u₂ v₂) :=
(onObjects : C → D)
(onMorphisms : Π {X Y : C}, (X ⟶ Y) → ((onObjects X) ⟶ (onObjects Y)))
(identities : ∀ (X : C), onMorphisms (𝟙 X) = 𝟙 (onObjects X) . obviously)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now you have categories.functor.Functor, I think it should be just category_theory.functor.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @johoelzl, I've made all the changes you suggested. I'm very happy to adjust naming to match mathlib conventions better, although would like to err on the side of longer names over ambiguous or not-immediately-obvious ones, where reasonable.

(functoriality : ∀ {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z), onMorphisms (f ≫ g) = (onMorphisms f) ≫ (onMorphisms g) . obviously)

make_lemma Functor.identities
make_lemma Functor.functoriality
attribute [simp,ematch] Functor.functoriality_lemma Functor.identities_lemma

infixr ` +> `:70 := Functor.onObjects
infixr ` &> `:70 := Functor.onMorphisms -- switch to ▹?
infixr ` ↝ `:70 := Functor -- type as \lea

definition IdentityFunctor (C : Type u₁) [category.{u₁ v₁} C] : C ↝ C :=
{ onObjects := id,
onMorphisms := λ _ _ f, f,
identities := begin
-- `obviously'` says:
intros,
refl
end,
functoriality := begin
-- `obviously'` says:
intros,
refl
end }

instance (C) [category C] : has_one (C ↝ C) :=
{ one := IdentityFunctor C }

variable {C : Type u₁}
variable [𝒞 : category.{u₁ v₁} C]
include 𝒞

@[simp] lemma IdentityFunctor.onObjects (X : C) : (IdentityFunctor C) +> X = X := by refl
@[simp] lemma IdentityFunctor.onMorphisms {X Y : C} (f : X ⟶ Y) : (IdentityFunctor C) &> f = f := by refl

variable {D : Type u₂}
variable [𝒟 : category.{u₂ v₂} D]
variable {E : Type u₃}
variable [ℰ : category.{u₃ v₃} E]
include 𝒟 ℰ

definition FunctorComposition (F : C ↝ D) (G : D ↝ E) : C ↝ E :=
{ onObjects := λ X, G +> (F +> X),
onMorphisms := λ _ _ f, G &> (F &> f),
identities := begin
-- `obviously'` says:
intros,
simp,
end,
functoriality := begin
-- `obviously'` says:
intros,
simp
end }
infixr ` ⋙ `:80 := FunctorComposition

@[simp] lemma FunctorComposition.onObjects (F : C ↝ D) (G : D ↝ E) (X : C) : (F ⋙ G) +> X = G +> (F +> X) := by refl
@[simp] lemma FunctorComposition.onMorphisms (F : C ↝ D) (G : D ↝ E) (X Y : C) (f : X ⟶ Y) : (F ⋙ G) &> f = G.onMorphisms (F &> f) := by refl

end categories.functor
101 changes: 101 additions & 0 deletions category_theory/natural_transformation.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
-- Copyright (c) 2017 Scott Morrison. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Tim Baumann, Stephen Morgan, Scott Morrison

import .functor

open categories
open categories.functor

namespace categories.natural_transformation

universes u₁ v₁ u₂ v₂ u₃ v₃

variable {C : Type u₁}
variable [𝒞 : category.{u₁ v₁} C]
variable {D : Type u₂}
variable [𝒟 : category.{u₂ v₂} D]
include 𝒞 𝒟

structure NaturalTransformation (F G : C ↝ D) : Type (max u₁ v₂) :=
(components: Π X : C, (F +> X) ⟶ (G +> X))
(naturality: ∀ {X Y : C} (f : X ⟶ Y), (F &> f) ≫ (components Y) = (components X) ≫ (G &> f) . obviously)

make_lemma NaturalTransformation.naturality
attribute [ematch] NaturalTransformation.naturality_lemma

infixr ` ⟹ `:50 := NaturalTransformation -- type as \==> or ⟹

definition IdentityNaturalTransformation (F : C ↝ D) : F ⟹ F :=
{ components := λ X, 𝟙 (F +> X),
naturality := begin
-- `obviously'` says:
intros,
simp
end }

@[simp] lemma IdentityNaturalTransformation.components (F : C ↝ D) (X : C) : (IdentityNaturalTransformation F).components X = 𝟙 (F +> X) := by refl

variables {F G H : C ↝ D}

-- We'll want to be able to prove that two natural transformations are equal if they are componentwise equal.
@[extensionality] lemma NaturalTransformations_componentwise_equal (α β : F ⟹ G) (w : ∀ X : C, α.components X = β.components X) : α = β :=
begin
induction α with α_components α_naturality,
induction β with β_components β_naturality,
have hc : α_components = β_components := funext w,
subst hc
end

definition vertical_composition_of_NaturalTransformations (α : F ⟹ G) (β : G ⟹ H) : F ⟹ H :=
{ components := λ X, (α.components X) ≫ (β.components X),
naturality := begin
-- `obviously'` says:
intros,
simp,
erw [←category.associativity_lemma, NaturalTransformation.naturality_lemma, category.associativity_lemma, ←NaturalTransformation.naturality_lemma]
end }

notation α `⊟` β:80 := vertical_composition_of_NaturalTransformations α β

@[simp,ematch] lemma vertical_composition_of_NaturalTransformations.components (α : F ⟹ G) (β : G ⟹ H) (X : C) : (α ⊟ β).components X = (α.components X) ≫ (β.components X) := by refl

variable {E : Type u₃}
variable [ℰ : category.{u₃ v₃} E]
include ℰ

instance (F : C ↝ D) : has_one (F ⟹ F) :=
{ one := IdentityNaturalTransformation F }

definition horizontal_composition_of_NaturalTransformations {F G : C ↝ D} {H I : D ↝ E} (α : F ⟹ G) (β : H ⟹ I) : (F ⋙ H) ⟹ (G ⋙ I) :=
{ components := λ X : C, (β.components (F +> X)) ≫ (I &> (α.components X)),
naturality := begin
-- `obviously'` says:
intros,
simp,
-- Actually, obviously doesn't use exactly this sequence of rewrites, but achieves the same result
rw [← category.associativity_lemma],
rw [NaturalTransformation.naturality_lemma],
rw [category.associativity_lemma],
conv { to_rhs, rw [← Functor.functoriality_lemma] },
rw [← α.naturality_lemma],
rw [Functor.functoriality_lemma],
end }

notation α `◫` β:80 := horizontal_composition_of_NaturalTransformations α β

@[simp,ematch] lemma horizontal_composition_of_NaturalTransformations.components {F G : C ↝ D} {H I : D ↝ E} (α : F ⟹ G) (β : H ⟹ I) (X : C) : (α ◫ β).components X = (β.components (F +> X)) ≫ (I &> (α.components X)) := by refl

@[ematch] lemma NaturalTransformation.exchange {F G H : C ↝ D} {I J K : D ↝ E} (α : F ⟹ G) (β : G ⟹ H) (γ : I ⟹ J) (δ : J ⟹ K) : ((α ⊟ β) ◫ (γ ⊟ δ)) = ((α ◫ γ) ⊟ (β ◫ δ)) :=
begin
-- `obviously'` says:
apply categories.natural_transformation.NaturalTransformations_componentwise_equal,
intros,
simp,
-- again, this isn't actually what obviously says, but it achieves the same effect.
conv {to_lhs, congr, skip, rw [←category.associativity_lemma] },
rw [←NaturalTransformation.naturality_lemma],
rw [category.associativity_lemma],
end

end categories.natural_transformation
78 changes: 78 additions & 0 deletions docs/theories/categories.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Maths in Lean : category theory

The `category` typeclass is defined in [category_theory/category.lean](https://github.com/leanprover/mathlib/blob/master/category_theory/category.lean).
It depends on the type of the objects, so for example we might write `category (Type u)` if we're talking about a category whose objects are types (in universe `u`).
Some care is needed with universes (see the section [Universes](##markdown-header-universes)), and end users may often prefer the abbreviations `small_category` and `large_category`.

Functors (which are a structure, not a typeclass) are defined in [category_theory/functor/default.lean](https://github.com/leanprover/mathlib/blob/master/category_theory/functor/default.lean),
along with identity functors and functor composition.

Natural transformations, and their compositions, are defined in [category_theory/natural_transformation.lean](https://github.com/leanprover/mathlib/blob/master/category_theory/natural_transformation.lean).

## Universes

Unfortunately in a category theory library we have to deal with universes carefully. We have the following:

````
category.{u₁ v₁} : Type (max (u₁+1) (v₁+1))
C : category.{u₁ v₁}
D : category.{u₂ v₂}
Functor C D : Type (max u₁ u₂ v₁ v₂)
F G : Functor C D
NaturalTransformation F G : Type (max u₁ v₂)
FunctorCategory C D : category.{(max u₁ u₂ v₁ v₂) (max u₁ v₂)}
````

Note then that if we specialise to small categories, where `uᵢ = vᵢ`, then `FunctorCategory C D : category.{(max u₁ u₂) (max u₁ u₂)}`, and so is again a small category.
If `C` is a small category and `D` is a large category (i.e. `u₂ = v₂+1`), and `v₂ = v₁` then we have `FunctorCategory C D : category.{v₁+1 v₁}` so is again a large category.

Whenever you want to write code uniformly for small and large categories (which you do by talking about categories whose universe levels `u` and `v` are unrelated), you will find that
Lean's `variable` mechanism doesn't always work, and the following trick is often helpful:

````
variables {C : Type u₁} [𝒞 : category.{u₁ v₁} C]
variables {D : Type u₂} [𝒟 : category.{u₂ v₂} D]
include 𝒞 𝒟
````


## Notation

### Categories

We use the `⟶` (`\hom`) arrow to denote sets of morphisms, as in `X ⟶ Y`.
This leaves the actual category implicit; it is inferred from the type of X and Y by typeclass inference.

We use `𝟙` (`\b1`) to denote identity morphisms, as in `𝟙 X`.

We use `≫` (`\gg`) to denote composition of morphisms, as in `f ≫ g`, which means "`f` followed by `g`".
This is the opposite order than the usual convention (which is lame).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mathematicians are going to love you for this [/sarcasm]. Is it possible to also have notation for "ordinary" composition? Would it even be possible to overload \circ? Or will madness ensue? If so, then preferably something that reminds of \circ.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will investigate overloading \circ, or finding something nearby.

I'd been hoping that everyone would be relieved that starting to write mathematics in Lean gave them a good opportunity to abandon the no-good-terrible-horrible \circ notation after so many years. :-)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regardless of one's personal preferences, for the purpose of formalizing existing mathematics, it's greatly helpful if the Lean notation is as similar as possible to the source notation; and that will almost always require having some form of applicative-order composition notation available.

In my own project, I just write local notation f ` ∘ `:80 g:80 := g ≫ f at the top of most files, and this works pretty well for me, though I occasionally need to write function.comp for the usual operator. Not a very good solution for the library, though.


### Isomorphisms
We use `≅` for isomorphisms.

### Functors
We use `↝` (`\leadsto` or `\lea` or `\r~`) to denote functors, as in `C ↝ D` for the type of functors from `C` to `D`.
Unfortunately Johannes reserved `⇒` (`\functor` or `\func`) in core: https://github.com/leanprover/lean/blob/master/library/init/relator.lean, so we can't use that here.
Perhaps this is unnecessary, and it's better to just write `Functor C D`.

Unfortunately, writing application of functors on objects and morphisms merely by function application is problematic.
To do either, we need to use a coercion to a function type, and we aren't allowed to do both this way.
Even doing one (probably application to objects) causes some serious problems to automation. I'll have one more go at this,
but in the meantime:

We use `+>` to denote the action of a functor on an object, as in `F +> X`.
We use `&>` to denote the action of a functor on a morphism, as in `F &> f`.

Functor composition can be written as `F ⋙ G`.

### Natural transformations
We use `⟹` (`\nattrans` or `\==>`) to denote the type of natural transformations, e.g. `F ⟹ G`.
We use `⇔` (`\<=>`) to denote the type of natural isomorphisms.

Unfortunately, while we'd like to write components of natural transformations via function application (e.g. `τ X`),
this requires coercions to function types, which I don't like.

For now we just write out `τ.components X`.

For vertical and horiztonal composition of natural transformations we "cutely" use `⊟` (`\boxminus`) and `◫` (currently untypeable, but we could ask for `\boxvert`).
41 changes: 41 additions & 0 deletions tactic/make_lemma.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-- Copyright (c) 2017 Scott Morrison. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.
-- Authors: Scott Morrison

import data.buffer.parser

open lean.parser tactic interactive parser

@[user_attribute] meta def field_lemma_attr : user_attribute :=
{ name := `field_lemma, descr := "This definition was automatically generated from a structure field by `make_lemma`." }


/- Make lemma takes a structure field, and makes a new, definitionally simplified copy of it, appending `_lemma` to the name.
The main application is to provide clean versions of structure fields that have been tagged with an auto_param. -/
meta def make_lemma (d : declaration) (new_name : name) : tactic unit :=
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name of this tactic is extremely generic. How about mk_structure_lemma or restate_axiom?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done: restate_axiom.

do (levels, type, value, reducibility, trusted) ← pure (match d.to_definition with
| declaration.defn name levels type value reducibility trusted :=
(levels, type, value, reducibility, trusted)
| _ := undefined
end),
(s, u) ← mk_simp_set ff [] [],
new_type ← (s.dsimplify [] type) <|> pure (type),
updateex_env $ λ env, env.add (declaration.defn new_name levels new_type value reducibility trusted),
field_lemma_attr.set new_name () tt

private meta def name_lemma (n : name) :=
match n.components.reverse with
| last :: most := mk_str_name n.get_prefix (last.to_string ++ "_lemma")
| nil := undefined
end

@[user_command] meta def make_lemma_cmd (meta_info : decl_meta_info)
(_ : parse $ tk "make_lemma") : lean.parser unit :=
do from_lemma ← ident,
from_lemma_fully_qualified ← resolve_constant from_lemma,
d ← get_decl from_lemma_fully_qualified <|>
fail ("declaration " ++ to_string from_lemma ++ " not found"),
do {
make_lemma d (name_lemma from_lemma_fully_qualified)
}.