Skip to content

Commit

Permalink
feat(tactic/reassoc_axiom): produce associativity-friendly lemmas in …
Browse files Browse the repository at this point in the history
…category theory (#1341)
  • Loading branch information
cipher1024 committed Aug 19, 2019
1 parent 8f09b0f commit 6fbcc04
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 54 deletions.
44 changes: 44 additions & 0 deletions docs/tactics.md
Original file line number Diff line number Diff line change
Expand Up @@ -1058,6 +1058,50 @@ localized "attribute [simp] le_refl" in le

`rotate` moves the first goal to the back. `rotate n` will do this `n` times.

### The `reassoc` attribute

The `reassoc` attribute can be applied to a lemma

```lean
@[reassoc]
lemma some_lemma : foo ≫ bar = baz := ...
```

and produce

```lean
lemma some_lemma_assoc {Y : C} (f : X ⟶ Y) : foo ≫ bar ≫ f = baz ≫ f := ...
```

The name of the produced lemma can be specified with `@[reassoc other_lemma_name]`. If
`simp` is added first, the generated lemma will also have the `simp` attribute.

### The `reassoc_axiom` command

When declaring a class of categories, the axioms can be reformulated to be more amenable
to manipulation in right associated expressions:

```lean
class some_class (C : Type) [category C] :=
(foo : Π X : C, X ⟶ X)
(bar : ∀ {X Y : C} (f : X ⟶ Y), foo X ≫ f = f ≫ foo Y)
reassoc_axiom some_class.bar
```

The above will produce:

```lean
lemma some_class.bar_assoc {Z : C} (g : Y ⟶ Z) :
foo X ≫ f ≫ g = f ≫ foo Y ≫ g := ...
```

Here too, the `reassoc` attribute can be used instead. It works well when combined with
`simp`:

```lean
attribute [simp, reassoc] some_class.bar
```
### sanity_check

The `#sanity_check` command checks for common mistakes in the current file or in all of mathlib, respectively.
Expand Down
24 changes: 4 additions & 20 deletions src/category_theory/adjunction/basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -84,38 +84,22 @@ begin
simp
end

@[simp] lemma left_triangle_components :
@[simp, reassoc] lemma left_triangle_components :
F.map (adj.unit.app X) ≫ adj.counit.app (F.obj X) = 𝟙 (F.obj X) :=
congr_arg (λ (t : nat_trans _ (functor.id C ⋙ F)), t.app X) adj.left_triangle

@[simp] lemma right_triangle_components {Y : D} :
@[simp, reassoc] lemma right_triangle_components {Y : D} :
adj.unit.app (G.obj Y) ≫ G.map (adj.counit.app Y) = 𝟙 (G.obj Y) :=
congr_arg (λ (t : nat_trans _ (G ⋙ functor.id C)), t.app Y) adj.right_triangle

@[simp] lemma left_triangle_components_assoc {Z : D} (f : F.obj X ⟶ Z) :
F.map (adj.unit.app X) ≫ adj.counit.app (F.obj X) ≫ f = f :=
by { rw [←assoc], dsimp, simp }

@[simp] lemma right_triangle_components_assoc {Y : D} {Z : C} (f : G.obj Y ⟶ Z) :
adj.unit.app (G.obj Y) ≫ G.map (adj.counit.app Y) ≫ f = f :=
by { rw [←assoc], dsimp, simp }

@[simp] lemma counit_naturality {X Y : D} (f : X ⟶ Y) :
@[simp, reassoc] lemma counit_naturality {X Y : D} (f : X ⟶ Y) :
F.map (G.map f) ≫ (adj.counit).app Y = (adj.counit).app X ≫ f :=
adj.counit.naturality f

@[simp] lemma unit_naturality {X Y : C} (f : X ⟶ Y) :
@[simp, reassoc] lemma unit_naturality {X Y : C} (f : X ⟶ Y) :
(adj.unit).app X ≫ G.map (F.map f) = f ≫ (adj.unit).app Y :=
(adj.unit.naturality f).symm

@[simp] lemma counit_naturality_assoc {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
F.map (G.map f) ≫ (adj.counit).app Y ≫ g = (adj.counit).app X ≫ f ≫ g :=
by { rw [←assoc], dsimp, simp }

@[simp] lemma unit_naturality_assoc {X Y Z : C} (f : X ⟶ Y) (g : G.obj (F.obj Y) ⟶ Z) :
(adj.unit).app X ≫ G.map (F.map f) ≫ g = f ≫ (adj.unit).app Y ≫ g :=
by { rw [←assoc], dsimp, simp }

end

end adjunction
Expand Down
6 changes: 2 additions & 4 deletions src/category_theory/eq_to_hom.lean
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Authors: Reid Barton, Scott Morrison
import category_theory.isomorphism
import category_theory.functor_category
import category_theory.opposites
import tactic.reassoc_axiom

universes v v' u u' -- declare the `v`'s first; see `category_theory.category` for an explanation

Expand All @@ -18,12 +19,9 @@ include 𝒞
def eq_to_hom {X Y : C} (p : X = Y) : X ⟶ Y := by rw p; exact 𝟙 _

@[simp] lemma eq_to_hom_refl (X : C) (p : X = X) : eq_to_hom p = 𝟙 X := rfl
@[simp] lemma eq_to_hom_trans {X Y Z : C} (p : X = Y) (q : Y = Z) :
@[simp, reassoc] lemma eq_to_hom_trans {X Y Z : C} (p : X = Y) (q : Y = Z) :
eq_to_hom p ≫ eq_to_hom q = eq_to_hom (p.trans q) :=
by cases p; cases q; simp
@[simp] lemma eq_to_hom_trans_assoc {X Y Z W : C} (p : X = Y) (q : Y = Z) (f : Z ⟶ W) :
eq_to_hom p ≫ (eq_to_hom q ≫ f) = eq_to_hom (p.trans q) ≫ f :=
by cases p; cases q; simp

def eq_to_iso {X Y : C} (p : X = Y) : X ≅ Y :=
⟨eq_to_hom p, eq_to_hom p.symm, by simp, by simp⟩
Expand Down
9 changes: 2 additions & 7 deletions src/category_theory/isomorphism.lean
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tim Baumann, Stephen Morgan, Scott Morrison, Floris van Doorn
-/
import category_theory.functor
import tactic.reassoc_axiom

/-!
# Isomorphisms
Expand Down Expand Up @@ -42,7 +43,7 @@ structure iso {C : Type u} [category.{v} C] (X Y : C) :=

restate_axiom iso.hom_inv_id'
restate_axiom iso.inv_hom_id'
attribute [simp] iso.hom_inv_id iso.inv_hom_id
attribute [simp, reassoc] iso.hom_inv_id iso.inv_hom_id

infixr ` ≅ `:10 := iso -- type as \cong or \iso

Expand All @@ -52,12 +53,6 @@ variables {X Y Z : C}

namespace iso

@[simp] lemma hom_inv_id_assoc (α : X ≅ Y) (f : X ⟶ Z) : α.hom ≫ α.inv ≫ f = f :=
by rw [←category.assoc, α.hom_inv_id, category.id_comp]

@[simp] lemma inv_hom_id_assoc (α : X ≅ Y) (f : Y ⟶ Z) : α.inv ≫ α.hom ≫ f = f :=
by rw [←category.assoc, α.inv_hom_id, category.id_comp]

@[extensionality] lemma ext (α β : X ≅ Y) (w : α.hom = β.hom) : α = β :=
suffices α.inv = β.inv, by cases α; cases β; cc,
calc α.inv
Expand Down
31 changes: 8 additions & 23 deletions src/category_theory/limits/limits.lean
Original file line number Diff line number Diff line change
Expand Up @@ -548,22 +548,19 @@ def colimit.desc (F : J ⥤ C) [has_colimit F] (c : cocone F) : colimit F ⟶ c.
@[simp] lemma colimit.is_colimit_desc {F : J ⥤ C} [has_colimit F] (c : cocone F) :
(colimit.is_colimit F).desc c = colimit.desc F c := rfl

@[simp] lemma colimit.ι_desc {F : J ⥤ C} [has_colimit F] (c : cocone F) (j : J) :
colimit.ι F j ≫ colimit.desc F c = c.ι.app j :=
is_colimit.fac _ c j

/--
We have lots of lemmas describing how to simplify `colimit.ι F j ≫ _`,
and combined with `colimit.ext` we rely on these lemmas for many calculations.
However, since `category.assoc` is a `@[simp]` lemma, often expressions are
right associated, and it's hard to apply these lemmas about `colimit.ι`.
We thus define some additional `@[simp]` lemmas, with an arbitrary extra morphism.
We thus use `reassoc` to define additional `@[simp]` lemmas, with an arbitrary extra morphism.
(see `tactic/reassoc_axiom.lean`)
-/
@[simp] lemma colimit.ι_desc_assoc {F : J ⥤ C} [has_colimit F] (c : cocone F) (j : J) {Y : C} (f : c.X ⟶ Y) :
colimit.ι F j ≫ colimit.desc F c ≫ f = c.ι.app j ≫ f :=
by rw [←category.assoc, colimit.ι_desc]
@[simp, reassoc] lemma colimit.ι_desc {F : J ⥤ C} [has_colimit F] (c : cocone F) (j : J) :
colimit.ι F j ≫ colimit.desc F c = c.ι.app j :=
is_colimit.fac _ c j

def colimit.cocone_morphism {F : J ⥤ C} [has_colimit F] (c : cocone F) :
cocone_morphism (colimit.cocone F) c :=
Expand Down Expand Up @@ -621,13 +618,9 @@ colimit.desc (E ⋙ F)
{ X := colimit F,
ι := { app := λ k, colimit.ι F (E.obj k) } }

@[simp] lemma colimit.ι_pre (k : K) : colimit.ι (E ⋙ F) k ≫ colimit.pre F E = colimit.ι F (E.obj k) :=
@[simp, reassoc] lemma colimit.ι_pre (k : K) : colimit.ι (E ⋙ F) k ≫ colimit.pre F E = colimit.ι F (E.obj k) :=
by erw is_colimit.fac

@[simp] lemma colimit.ι_pre_assoc (k : K) {Z : C} (f : colimit F ⟶ Z) :
colimit.ι (E ⋙ F) k ≫ (colimit.pre F E) ≫ f = ((colimit.ι F (E.obj k)) : (E ⋙ F).obj k ⟶ colimit F) ≫ f :=
by rw [←category.assoc, colimit.ι_pre]

@[simp] lemma colimit.pre_desc (c : cocone F) :
colimit.pre F E ≫ colimit.desc F c = colimit.desc (E ⋙ F) (c.whisker E) :=
by ext; rw [←assoc, colimit.ι_pre]; simp
Expand Down Expand Up @@ -659,13 +652,9 @@ colimit.desc (F ⋙ G)
naturality' :=
by intros j j' f; erw [←G.map_comp, limits.cocone.w, comp_id]; refl } }

@[simp] lemma colimit.ι_post (j : J) : colimit.ι (F ⋙ G) j ≫ colimit.post F G = G.map (colimit.ι F j) :=
@[simp, reassoc] lemma colimit.ι_post (j : J) : colimit.ι (F ⋙ G) j ≫ colimit.post F G = G.map (colimit.ι F j) :=
by erw is_colimit.fac

@[simp] lemma colimit.ι_post_assoc (j : J) {Y : D} (f : G.obj (colimit F) ⟶ Y) :
colimit.ι (F ⋙ G) j ≫ colimit.post F G ≫ f = G.map (colimit.ι F j) ≫ f :=
by rw [←category.assoc, colimit.ι_post]

@[simp] lemma colimit.post_desc (c : cocone F) :
colimit.post F G ≫ G.map (colimit.desc F c) = colimit.desc (F ⋙ G) (G.map_cocone c) :=
by ext; rw [←assoc, colimit.ι_post, ←G.map_comp, colimit.ι_desc, colimit.ι_desc]; refl
Expand Down Expand Up @@ -738,13 +727,9 @@ def colim : (J ⥤ C) ⥤ C :=

variables {F} {G : J ⥤ C} (α : F ⟶ G)

@[simp] lemma colim.ι_map (j : J) : colimit.ι F j ≫ colim.map α = α.app j ≫ colimit.ι G j :=
@[simp, reassoc] lemma colim.ι_map (j : J) : colimit.ι F j ≫ colim.map α = α.app j ≫ colimit.ι G j :=
by apply is_colimit.fac

@[simp] lemma colim.ι_map_assoc (j : J) {Y : C} (f : colimit G ⟶ Y) :
colimit.ι F j ≫ colim.map α ≫ f = α.app j ≫ colimit.ι G j ≫ f :=
by rw [←category.assoc, colim.ι_map, category.assoc]

@[simp] lemma colimit.map_desc (c : cocone G) :
colim.map α ≫ colimit.desc G c = colimit.desc F ((cocones.precompose α).obj c) :=
by ext; rw [←assoc, colim.ι_map, assoc, colimit.ι_desc, colimit.ι_desc]; refl
Expand Down
121 changes: 121 additions & 0 deletions src/tactic/reassoc_axiom.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/-
Copyright (c) 2019 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author(s): Simon Hudon
-/
import category_theory.category

/-!
Reformulate category-theoretic axioms in a more associativity-friendly way.
## The `reassoc` attribute
The `reassoc` attribute can be applied to a lemma
```lean
@[reassoc]
lemma some_lemma : foo ≫ bar = baz := ...
```
and produce
```lean
lemma some_lemma_assoc {Y : C} (f : X ⟶ Y) : foo ≫ bar ≫ f = baz ≫ f := ...
```
The name of the produced lemma can be specified with `@[reassoc other_lemma_name]`. If
`simp` is added first, the generated lemma will also have the `simp` attribute.
## The `reassoc_axiom` command
When declaring a class of categories, the axioms can be reformulated to be more amenable
to manipulation in right associated expressions:
```
class some_class (C : Type) [category C] :=
(foo : Π X : C, X ⟶ X)
(bar : ∀ {X Y : C} (f : X ⟶ Y), foo X ≫ f = f ≫ foo Y)
reassoc_axiom some_class.bar
```
Here too, the `reassoc` attribute can be used instead. It works well when combined with
`simp`:
```
attribute [simp, reassoc] some_class.bar
```
-/

namespace tactic

open interactive lean.parser category_theory

meta def reassoc_axiom (n : name) (n' : name := n.append_suffix "_assoc") : tactic unit :=
do d ← get_decl n,
let ls := d.univ_params.map level.param,
let c := @expr.const tt n ls,
(vs,t) ← infer_type c >>= mk_local_pis,
(vs',t) ← whnf t >>= mk_local_pis,
let vs := vs ++ vs',
(lhs,rhs) ← match_eq t,
`(@category_struct.comp _ %%struct_inst _ _ _ _ _) ← pure lhs,
`(@has_hom.hom _ %%hom_inst %%X %%Y) ← infer_type lhs,
C ← infer_type X,
X' ← mk_local' `X' binder_info.implicit C,
ft ← to_expr ``(@has_hom.hom _ %%hom_inst %%Y %%X'),
f' ← mk_local_def `f' ft,
t' ← to_expr ``(@category_struct.comp _ %%struct_inst _ _ _%%lhs %%f' = @category_struct.comp _ %%struct_inst _ _ _ %%rhs %%f'),
let c' := c.mk_app vs,
(_,pr) ← solve_aux t' (rewrite_target c'; reflexivity),
pr ← instantiate_mvars pr,
let s := simp_lemmas.mk,
s ← s.add_simp ``category.assoc,
s ← s.add_simp ``category.id_comp,
s ← s.add_simp ``category.comp_id,
(t'',pr') ← simplify s [] t',
pr' ← mk_eq_mp pr' pr,
t'' ← pis (vs ++ [X',f']) t'',
pr' ← lambdas (vs ++ [X',f']) pr',
add_decl $ declaration.thm n' d.univ_params t'' (pure pr'),
copy_attribute `simp n tt n'

/--
On the following lemma:
```
@[reassoc]
lemma foo_bar : foo ≫ bar = foo := ...
```
generates
```
lemma foo_bar_assoc {Z} {x : Y ⟶ Z} : foo ≫ bar ≫ x = foo ≫ x := ...
```
The name of `foo_bar_assoc` can also be selected with @[reassoc new_name]
-/
@[user_attribute]
meta def reassoc_attr : user_attribute unit (option name) :=
{ name := `reassoc,
descr := "create a companion lemma for associativity-aware rewriting",
parser := optional ident,
after_set := some (λ n _ _,
do some n' ← reassoc_attr.get_param n | reassoc_axiom n,
reassoc_axiom n $ n.get_prefix ++ n' ) }

/--
```
reassoc_axiom my_axiom
```
produces the lemma `my_axiom_assoc` which transforms a statement of the
form `x ≫ y = z` into `x ≫ y ≫ k = z ≫ k`.
-/
@[user_command]
meta def reassoc_cmd (_ : parse $ tk "reassoc_axiom") : lean.parser unit :=
do n ← ident,
of_tactic' $
do n ← resolve_constant n,
reassoc_axiom n

end tactic

0 comments on commit 6fbcc04

Please sign in to comment.