Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 6fbcc04

Browse files
authored
feat(tactic/reassoc_axiom): produce associativity-friendly lemmas in category theory (#1341)
1 parent 8f09b0f commit 6fbcc04

File tree

6 files changed

+181
-54
lines changed

6 files changed

+181
-54
lines changed

docs/tactics.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,50 @@ localized "attribute [simp] le_refl" in le
10581058

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

1061+
### The `reassoc` attribute
1062+
1063+
The `reassoc` attribute can be applied to a lemma
1064+
1065+
```lean
1066+
@[reassoc]
1067+
lemma some_lemma : foo ≫ bar = baz := ...
1068+
```
1069+
1070+
and produce
1071+
1072+
```lean
1073+
lemma some_lemma_assoc {Y : C} (f : X ⟶ Y) : foo ≫ bar ≫ f = baz ≫ f := ...
1074+
```
1075+
1076+
The name of the produced lemma can be specified with `@[reassoc other_lemma_name]`. If
1077+
`simp` is added first, the generated lemma will also have the `simp` attribute.
1078+
1079+
### The `reassoc_axiom` command
1080+
1081+
When declaring a class of categories, the axioms can be reformulated to be more amenable
1082+
to manipulation in right associated expressions:
1083+
1084+
```lean
1085+
class some_class (C : Type) [category C] :=
1086+
(foo : Π X : C, X ⟶ X)
1087+
(bar : ∀ {X Y : C} (f : X ⟶ Y), foo X ≫ f = f ≫ foo Y)
1088+
1089+
reassoc_axiom some_class.bar
1090+
```
1091+
1092+
The above will produce:
1093+
1094+
```lean
1095+
lemma some_class.bar_assoc {Z : C} (g : Y ⟶ Z) :
1096+
foo X ≫ f ≫ g = f ≫ foo Y ≫ g := ...
1097+
```
1098+
1099+
Here too, the `reassoc` attribute can be used instead. It works well when combined with
1100+
`simp`:
1101+
1102+
```lean
1103+
attribute [simp, reassoc] some_class.bar
1104+
```
10611105
### sanity_check
10621106

10631107
The `#sanity_check` command checks for common mistakes in the current file or in all of mathlib, respectively.

src/category_theory/adjunction/basic.lean

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,38 +84,22 @@ begin
8484
simp
8585
end
8686

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

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

95-
@[simp] lemma left_triangle_components_assoc {Z : D} (f : F.obj X ⟶ Z) :
96-
F.map (adj.unit.app X) ≫ adj.counit.app (F.obj X) ≫ f = f :=
97-
by { rw [←assoc], dsimp, simp }
98-
99-
@[simp] lemma right_triangle_components_assoc {Y : D} {Z : C} (f : G.obj Y ⟶ Z) :
100-
adj.unit.app (G.obj Y) ≫ G.map (adj.counit.app Y) ≫ f = f :=
101-
by { rw [←assoc], dsimp, simp }
102-
103-
@[simp] lemma counit_naturality {X Y : D} (f : X ⟶ Y) :
95+
@[simp, reassoc] lemma counit_naturality {X Y : D} (f : X ⟶ Y) :
10496
F.map (G.map f) ≫ (adj.counit).app Y = (adj.counit).app X ≫ f :=
10597
adj.counit.naturality f
10698

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

111-
@[simp] lemma counit_naturality_assoc {X Y Z : D} (f : X ⟶ Y) (g : Y ⟶ Z) :
112-
F.map (G.map f) ≫ (adj.counit).app Y ≫ g = (adj.counit).app X ≫ f ≫ g :=
113-
by { rw [←assoc], dsimp, simp }
114-
115-
@[simp] lemma unit_naturality_assoc {X Y Z : C} (f : X ⟶ Y) (g : G.obj (F.obj Y) ⟶ Z) :
116-
(adj.unit).app X ≫ G.map (F.map f) ≫ g = f ≫ (adj.unit).app Y ≫ g :=
117-
by { rw [←assoc], dsimp, simp }
118-
119103
end
120104

121105
end adjunction

src/category_theory/eq_to_hom.lean

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Authors: Reid Barton, Scott Morrison
66
import category_theory.isomorphism
77
import category_theory.functor_category
88
import category_theory.opposites
9+
import tactic.reassoc_axiom
910

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

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

2021
@[simp] lemma eq_to_hom_refl (X : C) (p : X = X) : eq_to_hom p = 𝟙 X := rfl
21-
@[simp] lemma eq_to_hom_trans {X Y Z : C} (p : X = Y) (q : Y = Z) :
22+
@[simp, reassoc] lemma eq_to_hom_trans {X Y Z : C} (p : X = Y) (q : Y = Z) :
2223
eq_to_hom p ≫ eq_to_hom q = eq_to_hom (p.trans q) :=
2324
by cases p; cases q; simp
24-
@[simp] lemma eq_to_hom_trans_assoc {X Y Z W : C} (p : X = Y) (q : Y = Z) (f : Z ⟶ W) :
25-
eq_to_hom p ≫ (eq_to_hom q ≫ f) = eq_to_hom (p.trans q) ≫ f :=
26-
by cases p; cases q; simp
2725

2826
def eq_to_iso {X Y : C} (p : X = Y) : X ≅ Y :=
2927
⟨eq_to_hom p, eq_to_hom p.symm, by simp, by simp⟩

src/category_theory/isomorphism.lean

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Tim Baumann, Stephen Morgan, Scott Morrison, Floris van Doorn
55
-/
66
import category_theory.functor
7+
import tactic.reassoc_axiom
78

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

4344
restate_axiom iso.hom_inv_id'
4445
restate_axiom iso.inv_hom_id'
45-
attribute [simp] iso.hom_inv_id iso.inv_hom_id
46+
attribute [simp, reassoc] iso.hom_inv_id iso.inv_hom_id
4647

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

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

5354
namespace iso
5455

55-
@[simp] lemma hom_inv_id_assoc (α : X ≅ Y) (f : X ⟶ Z) : α.hom ≫ α.inv ≫ f = f :=
56-
by rw [←category.assoc, α.hom_inv_id, category.id_comp]
57-
58-
@[simp] lemma inv_hom_id_assoc (α : X ≅ Y) (f : Y ⟶ Z) : α.inv ≫ α.hom ≫ f = f :=
59-
by rw [←category.assoc, α.inv_hom_id, category.id_comp]
60-
6156
@[extensionality] lemma ext (α β : X ≅ Y) (w : α.hom = β.hom) : α = β :=
6257
suffices α.inv = β.inv, by cases α; cases β; cc,
6358
calc α.inv

src/category_theory/limits/limits.lean

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -548,22 +548,19 @@ def colimit.desc (F : J ⥤ C) [has_colimit F] (c : cocone F) : colimit F ⟶ c.
548548
@[simp] lemma colimit.is_colimit_desc {F : J ⥤ C} [has_colimit F] (c : cocone F) :
549549
(colimit.is_colimit F).desc c = colimit.desc F c := rfl
550550

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

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

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

627-
@[simp] lemma colimit.ι_pre_assoc (k : K) {Z : C} (f : colimit F ⟶ Z) :
628-
colimit.ι (E ⋙ F) k ≫ (colimit.pre F E) ≫ f = ((colimit.ι F (E.obj k)) : (E ⋙ F).obj k ⟶ colimit F) ≫ f :=
629-
by rw [←category.assoc, colimit.ι_pre]
630-
631624
@[simp] lemma colimit.pre_desc (c : cocone F) :
632625
colimit.pre F E ≫ colimit.desc F c = colimit.desc (E ⋙ F) (c.whisker E) :=
633626
by ext; rw [←assoc, colimit.ι_pre]; simp
@@ -659,13 +652,9 @@ colimit.desc (F ⋙ G)
659652
naturality' :=
660653
by intros j j' f; erw [←G.map_comp, limits.cocone.w, comp_id]; refl } }
661654

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

665-
@[simp] lemma colimit.ι_post_assoc (j : J) {Y : D} (f : G.obj (colimit F) ⟶ Y) :
666-
colimit.ι (F ⋙ G) j ≫ colimit.post F G ≫ f = G.map (colimit.ι F j) ≫ f :=
667-
by rw [←category.assoc, colimit.ι_post]
668-
669658
@[simp] lemma colimit.post_desc (c : cocone F) :
670659
colimit.post F G ≫ G.map (colimit.desc F c) = colimit.desc (F ⋙ G) (G.map_cocone c) :=
671660
by ext; rw [←assoc, colimit.ι_post, ←G.map_comp, colimit.ι_desc, colimit.ι_desc]; refl
@@ -738,13 +727,9 @@ def colim : (J ⥤ C) ⥤ C :=
738727

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

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

744-
@[simp] lemma colim.ι_map_assoc (j : J) {Y : C} (f : colimit G ⟶ Y) :
745-
colimit.ι F j ≫ colim.map α ≫ f = α.app j ≫ colimit.ι G j ≫ f :=
746-
by rw [←category.assoc, colim.ι_map, category.assoc]
747-
748733
@[simp] lemma colimit.map_desc (c : cocone G) :
749734
colim.map α ≫ colimit.desc G c = colimit.desc F ((cocones.precompose α).obj c) :=
750735
by ext; rw [←assoc, colim.ι_map, assoc, colimit.ι_desc, colimit.ι_desc]; refl

src/tactic/reassoc_axiom.lean

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/-
2+
Copyright (c) 2019 Simon Hudon. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Author(s): Simon Hudon
5+
-/
6+
import category_theory.category
7+
8+
/-!
9+
Reformulate category-theoretic axioms in a more associativity-friendly way.
10+
11+
## The `reassoc` attribute
12+
13+
The `reassoc` attribute can be applied to a lemma
14+
15+
```lean
16+
@[reassoc]
17+
lemma some_lemma : foo ≫ bar = baz := ...
18+
```
19+
20+
and produce
21+
22+
```lean
23+
lemma some_lemma_assoc {Y : C} (f : X ⟶ Y) : foo ≫ bar ≫ f = baz ≫ f := ...
24+
```
25+
26+
The name of the produced lemma can be specified with `@[reassoc other_lemma_name]`. If
27+
`simp` is added first, the generated lemma will also have the `simp` attribute.
28+
29+
## The `reassoc_axiom` command
30+
31+
When declaring a class of categories, the axioms can be reformulated to be more amenable
32+
to manipulation in right associated expressions:
33+
34+
```
35+
class some_class (C : Type) [category C] :=
36+
(foo : Π X : C, X ⟶ X)
37+
(bar : ∀ {X Y : C} (f : X ⟶ Y), foo X ≫ f = f ≫ foo Y)
38+
39+
reassoc_axiom some_class.bar
40+
```
41+
42+
Here too, the `reassoc` attribute can be used instead. It works well when combined with
43+
`simp`:
44+
45+
```
46+
attribute [simp, reassoc] some_class.bar
47+
```
48+
-/
49+
50+
namespace tactic
51+
52+
open interactive lean.parser category_theory
53+
54+
meta def reassoc_axiom (n : name) (n' : name := n.append_suffix "_assoc") : tactic unit :=
55+
do d ← get_decl n,
56+
let ls := d.univ_params.map level.param,
57+
let c := @expr.const tt n ls,
58+
(vs,t) ← infer_type c >>= mk_local_pis,
59+
(vs',t) ← whnf t >>= mk_local_pis,
60+
let vs := vs ++ vs',
61+
(lhs,rhs) ← match_eq t,
62+
`(@category_struct.comp _ %%struct_inst _ _ _ _ _) ← pure lhs,
63+
`(@has_hom.hom _ %%hom_inst %%X %%Y) ← infer_type lhs,
64+
C ← infer_type X,
65+
X' ← mk_local' `X' binder_info.implicit C,
66+
ft ← to_expr ``(@has_hom.hom _ %%hom_inst %%Y %%X'),
67+
f' ← mk_local_def `f' ft,
68+
t' ← to_expr ``(@category_struct.comp _ %%struct_inst _ _ _%%lhs %%f' = @category_struct.comp _ %%struct_inst _ _ _ %%rhs %%f'),
69+
let c' := c.mk_app vs,
70+
(_,pr) ← solve_aux t' (rewrite_target c'; reflexivity),
71+
pr ← instantiate_mvars pr,
72+
let s := simp_lemmas.mk,
73+
s ← s.add_simp ``category.assoc,
74+
s ← s.add_simp ``category.id_comp,
75+
s ← s.add_simp ``category.comp_id,
76+
(t'',pr') ← simplify s [] t',
77+
pr' ← mk_eq_mp pr' pr,
78+
t'' ← pis (vs ++ [X',f']) t'',
79+
pr' ← lambdas (vs ++ [X',f']) pr',
80+
add_decl $ declaration.thm n' d.univ_params t'' (pure pr'),
81+
copy_attribute `simp n tt n'
82+
83+
/--
84+
On the following lemma:
85+
```
86+
@[reassoc]
87+
lemma foo_bar : foo ≫ bar = foo := ...
88+
```
89+
generates
90+
91+
```
92+
lemma foo_bar_assoc {Z} {x : Y ⟶ Z} : foo ≫ bar ≫ x = foo ≫ x := ...
93+
```
94+
95+
The name of `foo_bar_assoc` can also be selected with @[reassoc new_name]
96+
-/
97+
@[user_attribute]
98+
meta def reassoc_attr : user_attribute unit (option name) :=
99+
{ name := `reassoc,
100+
descr := "create a companion lemma for associativity-aware rewriting",
101+
parser := optional ident,
102+
after_set := some (λ n _ _,
103+
do some n' ← reassoc_attr.get_param n | reassoc_axiom n,
104+
reassoc_axiom n $ n.get_prefix ++ n' ) }
105+
106+
/--
107+
```
108+
reassoc_axiom my_axiom
109+
```
110+
111+
produces the lemma `my_axiom_assoc` which transforms a statement of the
112+
form `x ≫ y = z` into `x ≫ y ≫ k = z ≫ k`.
113+
-/
114+
@[user_command]
115+
meta def reassoc_cmd (_ : parse $ tk "reassoc_axiom") : lean.parser unit :=
116+
do n ← ident,
117+
of_tactic' $
118+
do n ← resolve_constant n,
119+
reassoc_axiom n
120+
121+
end tactic

0 commit comments

Comments
 (0)