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
[Merged by Bors] - feat(group_theory): use generic subobject_class
lemmas
#11758
Conversation
43d49ae
to
ffa91b7
Compare
714dd2d
to
33a06bf
Compare
The next part of my big refactoring plans: subobject classes in the same style as morphism classes. This PR introduces the following subclasses of `set_like`: * `one_mem_class`, `zero_mem_class`, `mul_mem_class`, `add_mem_class`, `inv_mem_class`, `neg_mem_class` * `submonoid_class`, `add_submonoid_class` * `subgroup_class`, `add_subgroup_class` * `subsemiring_class`, `subring_class`, `subfield_class` The main purpose of this refactor is that we can replace the wide variety of lemmas like `{add_submonoid,add_subgroup,subring,subfield,submodule,subwhatever}.{prod,sum}_mem` with a single `prod_mem` lemma that is generic over all types `B` that extend `submonoid`: ```lean @[to_additive] lemma prod_mem {M : Type*} [comm_monoid M] [set_like B M] [submonoid_class B M] {ι : Type*} {t : finset ι} {f : ι → M} (h : ∀c ∈ t, f c ∈ S) : ∏ c in t, f c ∈ S ``` ## API changes * When you extend a `struct subobject`, make sure to create a corresponding `subobject_class` instance. ## Upcoming PRs This PR splits out the first part of #11545, namely defining the subobject classes. I am planning these follow-up PRs for further parts of #11545: - [ ] make the subobject consistently implicit in `{add,mul}_mem` #11758 - [ ] remove duplicate instances like `subgroup.to_group` (replaced by the `subgroup_class.to_subgroup` instances that are added by this PR) #11759 - [ ] further deduplication such as `finsupp_sum_mem` ## Subclassing `set_like` Contrary to mathlib's typical subclass pattern, we don't extend `set_like`, but take a `set_like` instance parameter: ```lean class one_mem_class (S : Type*) (M : out_param $ Type*) [has_one M] [set_like S M] := (one_mem : ∀ (s : S), (1 : M) ∈ s) ``` instead of: ```lean class one_mem_class (S : Type*) (M : out_param $ Type*) [has_one M] extends set_like S M := (one_mem : ∀ (s : S), (1 : M) ∈ s) ``` The main reason is that this avoids some big defeq checks when typechecking e.g. `x * y : s`, where `s : S` and `[comm_group G] [subgroup_class S G]`. Namely, the type `coe_sort s` could be given by `subgroup_class → @@submonoid_class _ _ (comm_group.to_group.to_monoid) → set_like → has_coe_to_sort` or by `subgroup_class → @@submonoid_class _ _ (comm_group.to_comm_monoid.to_monoid) → set_like → has_coe_to_sort`. When checking that `has_mul` on the first type is the same as `has_mul` on the second type, those two inheritance paths are unified many times over ([sometimes exponentially many](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Why.20is.20.60int.2Ecast_abs.60.20so.20slow.3F/near/266945077)). So it's important to keep the size of types small, and therefore we avoid `extends`-based inheritance. ## Defeq fixes Adding instances like `subgroup_class.to_group` means that there are now two (defeq) group instances for `subgroup`. This makes some code more fragile, until we can replace `subgroup.to_group` with its more generic form in a follow-up PR. Especially when taking subgroups of subgroups I needed to help the elaborator in a few places. These should be minimally invasive for other uses of the code. ## Timeout fixes Some of the leaf files started timing out, so I made a couple of fixes. Generally these can be classed as: * `squeeze_simps` * Give inheritance `subX_class S M` → `X s` (where `s : S`) a lower prority than `Y s` → `X s` so that `subY_class S M` → `Y s` → `X s` is preferred over `subY_class S M` → `subX_class S M` → `X s`. This addresses slow unifications when `x : s`, `s` is a submonoid of `t`, which is itself a subgroup of `G`: existing code expects to go `subgroup → group → monoid`, which got changed to `subgroup_class → submonoid_class → monoid`; when this kind of unification issue appears in your type this results in slow unification. By tweaking the priorities, we help the elaborator find our preferred instance, avoiding the big defeq checks. (The real fix should of course be to fix the unifier so it doesn't become exponential in these kinds of cases.) * Split a long proof with duplication into smaller parts. This was basically my last resort. I decided to bump the limit for the `fails_quickly` linter for `measure_theory.Lp_meas.complete_space`, which apparently just barely goes over this limit now. The time difference was about 10%-20% for that specific instance. Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com> Co-authored-by: Riccardo Brasca <riccardo.brasca@gmail.com>
This PR/issue depends on: |
33a06bf
to
13108bd
Compare
The next part of my big refactoring plans: subobject classes in the same style as morphism classes. This PR introduces the following subclasses of `set_like`: * `one_mem_class`, `zero_mem_class`, `mul_mem_class`, `add_mem_class`, `inv_mem_class`, `neg_mem_class` * `submonoid_class`, `add_submonoid_class` * `subgroup_class`, `add_subgroup_class` * `subsemiring_class`, `subring_class`, `subfield_class` The main purpose of this refactor is that we can replace the wide variety of lemmas like `{add_submonoid,add_subgroup,subring,subfield,submodule,subwhatever}.{prod,sum}_mem` with a single `prod_mem` lemma that is generic over all types `B` that extend `submonoid`: ```lean @[to_additive] lemma prod_mem {M : Type*} [comm_monoid M] [set_like B M] [submonoid_class B M] {ι : Type*} {t : finset ι} {f : ι → M} (h : ∀c ∈ t, f c ∈ S) : ∏ c in t, f c ∈ S ``` ## API changes * When you extend a `struct subobject`, make sure to create a corresponding `subobject_class` instance. ## Upcoming PRs This PR splits out the first part of #11545, namely defining the subobject classes. I am planning these follow-up PRs for further parts of #11545: - [ ] make the subobject consistently implicit in `{add,mul}_mem` #11758 - [ ] remove duplicate instances like `subgroup.to_group` (replaced by the `subgroup_class.to_subgroup` instances that are added by this PR) #11759 - [ ] further deduplication such as `finsupp_sum_mem` ## Subclassing `set_like` Contrary to mathlib's typical subclass pattern, we don't extend `set_like`, but take a `set_like` instance parameter: ```lean class one_mem_class (S : Type*) (M : out_param $ Type*) [has_one M] [set_like S M] := (one_mem : ∀ (s : S), (1 : M) ∈ s) ``` instead of: ```lean class one_mem_class (S : Type*) (M : out_param $ Type*) [has_one M] extends set_like S M := (one_mem : ∀ (s : S), (1 : M) ∈ s) ``` The main reason is that this avoids some big defeq checks when typechecking e.g. `x * y : s`, where `s : S` and `[comm_group G] [subgroup_class S G]`. Namely, the type `coe_sort s` could be given by `subgroup_class → @@submonoid_class _ _ (comm_group.to_group.to_monoid) → set_like → has_coe_to_sort` or by `subgroup_class → @@submonoid_class _ _ (comm_group.to_comm_monoid.to_monoid) → set_like → has_coe_to_sort`. When checking that `has_mul` on the first type is the same as `has_mul` on the second type, those two inheritance paths are unified many times over ([sometimes exponentially many](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Why.20is.20.60int.2Ecast_abs.60.20so.20slow.3F/near/266945077)). So it's important to keep the size of types small, and therefore we avoid `extends`-based inheritance. ## Defeq fixes Adding instances like `subgroup_class.to_group` means that there are now two (defeq) group instances for `subgroup`. This makes some code more fragile, until we can replace `subgroup.to_group` with its more generic form in a follow-up PR. Especially when taking subgroups of subgroups I needed to help the elaborator in a few places. These should be minimally invasive for other uses of the code. ## Timeout fixes Some of the leaf files started timing out, so I made a couple of fixes. Generally these can be classed as: * `squeeze_simps` * Give inheritance `subX_class S M` → `X s` (where `s : S`) a lower prority than `Y s` → `X s` so that `subY_class S M` → `Y s` → `X s` is preferred over `subY_class S M` → `subX_class S M` → `X s`. This addresses slow unifications when `x : s`, `s` is a submonoid of `t`, which is itself a subgroup of `G`: existing code expects to go `subgroup → group → monoid`, which got changed to `subgroup_class → submonoid_class → monoid`; when this kind of unification issue appears in your type this results in slow unification. By tweaking the priorities, we help the elaborator find our preferred instance, avoiding the big defeq checks. (The real fix should of course be to fix the unifier so it doesn't become exponential in these kinds of cases.) * Split a long proof with duplication into smaller parts. This was basically my last resort. I decided to bump the limit for the `fails_quickly` linter for `measure_theory.Lp_meas.complete_space`, which apparently just barely goes over this limit now. The time difference was about 10%-20% for that specific instance. Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com> Co-authored-by: Riccardo Brasca <riccardo.brasca@gmail.com>
The next part of my big refactoring plans: subobject classes in the same style as morphism classes. This PR introduces the following subclasses of `set_like`: * `one_mem_class`, `zero_mem_class`, `mul_mem_class`, `add_mem_class`, `inv_mem_class`, `neg_mem_class` * `submonoid_class`, `add_submonoid_class` * `subgroup_class`, `add_subgroup_class` * `subsemiring_class`, `subring_class`, `subfield_class` The main purpose of this refactor is that we can replace the wide variety of lemmas like `{add_submonoid,add_subgroup,subring,subfield,submodule,subwhatever}.{prod,sum}_mem` with a single `prod_mem` lemma that is generic over all types `B` that extend `submonoid`: ```lean @[to_additive] lemma prod_mem {M : Type*} [comm_monoid M] [set_like B M] [submonoid_class B M] {ι : Type*} {t : finset ι} {f : ι → M} (h : ∀c ∈ t, f c ∈ S) : ∏ c in t, f c ∈ S ``` ## API changes * When you extend a `struct subobject`, make sure to create a corresponding `subobject_class` instance. ## Upcoming PRs This PR splits out the first part of #11545, namely defining the subobject classes. I am planning these follow-up PRs for further parts of #11545: - [ ] make the subobject consistently implicit in `{add,mul}_mem` #11758 - [ ] remove duplicate instances like `subgroup.to_group` (replaced by the `subgroup_class.to_subgroup` instances that are added by this PR) #11759 - [ ] further deduplication such as `finsupp_sum_mem` ## Subclassing `set_like` Contrary to mathlib's typical subclass pattern, we don't extend `set_like`, but take a `set_like` instance parameter: ```lean class one_mem_class (S : Type*) (M : out_param $ Type*) [has_one M] [set_like S M] := (one_mem : ∀ (s : S), (1 : M) ∈ s) ``` instead of: ```lean class one_mem_class (S : Type*) (M : out_param $ Type*) [has_one M] extends set_like S M := (one_mem : ∀ (s : S), (1 : M) ∈ s) ``` The main reason is that this avoids some big defeq checks when typechecking e.g. `x * y : s`, where `s : S` and `[comm_group G] [subgroup_class S G]`. Namely, the type `coe_sort s` could be given by `subgroup_class → @@submonoid_class _ _ (comm_group.to_group.to_monoid) → set_like → has_coe_to_sort` or by `subgroup_class → @@submonoid_class _ _ (comm_group.to_comm_monoid.to_monoid) → set_like → has_coe_to_sort`. When checking that `has_mul` on the first type is the same as `has_mul` on the second type, those two inheritance paths are unified many times over ([sometimes exponentially many](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Why.20is.20.60int.2Ecast_abs.60.20so.20slow.3F/near/266945077)). So it's important to keep the size of types small, and therefore we avoid `extends`-based inheritance. ## Defeq fixes Adding instances like `subgroup_class.to_group` means that there are now two (defeq) group instances for `subgroup`. This makes some code more fragile, until we can replace `subgroup.to_group` with its more generic form in a follow-up PR. Especially when taking subgroups of subgroups I needed to help the elaborator in a few places. These should be minimally invasive for other uses of the code. ## Timeout fixes Some of the leaf files started timing out, so I made a couple of fixes. Generally these can be classed as: * `squeeze_simps` * Give inheritance `subX_class S M` → `X s` (where `s : S`) a lower prority than `Y s` → `X s` so that `subY_class S M` → `Y s` → `X s` is preferred over `subY_class S M` → `subX_class S M` → `X s`. This addresses slow unifications when `x : s`, `s` is a submonoid of `t`, which is itself a subgroup of `G`: existing code expects to go `subgroup → group → monoid`, which got changed to `subgroup_class → submonoid_class → monoid`; when this kind of unification issue appears in your type this results in slow unification. By tweaking the priorities, we help the elaborator find our preferred instance, avoiding the big defeq checks. (The real fix should of course be to fix the unifier so it doesn't become exponential in these kinds of cases.) * Split a long proof with duplication into smaller parts. This was basically my last resort. I decided to bump the limit for the `fails_quickly` linter for `measure_theory.Lp_meas.complete_space`, which apparently just barely goes over this limit now. The time difference was about 10%-20% for that specific instance. Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com> Co-authored-by: Riccardo Brasca <riccardo.brasca@gmail.com>
The next part of my big refactoring plans: subobject classes in the same style as morphism classes. This PR introduces the following subclasses of `set_like`: * `one_mem_class`, `zero_mem_class`, `mul_mem_class`, `add_mem_class`, `inv_mem_class`, `neg_mem_class` * `submonoid_class`, `add_submonoid_class` * `subgroup_class`, `add_subgroup_class` * `subsemiring_class`, `subring_class`, `subfield_class` The main purpose of this refactor is that we can replace the wide variety of lemmas like `{add_submonoid,add_subgroup,subring,subfield,submodule,subwhatever}.{prod,sum}_mem` with a single `prod_mem` lemma that is generic over all types `B` that extend `submonoid`: ```lean @[to_additive] lemma prod_mem {M : Type*} [comm_monoid M] [set_like B M] [submonoid_class B M] {ι : Type*} {t : finset ι} {f : ι → M} (h : ∀c ∈ t, f c ∈ S) : ∏ c in t, f c ∈ S ``` ## API changes * When you extend a `struct subobject`, make sure to create a corresponding `subobject_class` instance. ## Upcoming PRs This PR splits out the first part of #11545, namely defining the subobject classes. I am planning these follow-up PRs for further parts of #11545: - [ ] make the subobject consistently implicit in `{add,mul}_mem` #11758 - [ ] remove duplicate instances like `subgroup.to_group` (replaced by the `subgroup_class.to_subgroup` instances that are added by this PR) #11759 - [ ] further deduplication such as `finsupp_sum_mem` ## Subclassing `set_like` Contrary to mathlib's typical subclass pattern, we don't extend `set_like`, but take a `set_like` instance parameter: ```lean class one_mem_class (S : Type*) (M : out_param $ Type*) [has_one M] [set_like S M] := (one_mem : ∀ (s : S), (1 : M) ∈ s) ``` instead of: ```lean class one_mem_class (S : Type*) (M : out_param $ Type*) [has_one M] extends set_like S M := (one_mem : ∀ (s : S), (1 : M) ∈ s) ``` The main reason is that this avoids some big defeq checks when typechecking e.g. `x * y : s`, where `s : S` and `[comm_group G] [subgroup_class S G]`. Namely, the type `coe_sort s` could be given by `subgroup_class → @@submonoid_class _ _ (comm_group.to_group.to_monoid) → set_like → has_coe_to_sort` or by `subgroup_class → @@submonoid_class _ _ (comm_group.to_comm_monoid.to_monoid) → set_like → has_coe_to_sort`. When checking that `has_mul` on the first type is the same as `has_mul` on the second type, those two inheritance paths are unified many times over ([sometimes exponentially many](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Why.20is.20.60int.2Ecast_abs.60.20so.20slow.3F/near/266945077)). So it's important to keep the size of types small, and therefore we avoid `extends`-based inheritance. ## Defeq fixes Adding instances like `subgroup_class.to_group` means that there are now two (defeq) group instances for `subgroup`. This makes some code more fragile, until we can replace `subgroup.to_group` with its more generic form in a follow-up PR. Especially when taking subgroups of subgroups I needed to help the elaborator in a few places. These should be minimally invasive for other uses of the code. ## Timeout fixes Some of the leaf files started timing out, so I made a couple of fixes. Generally these can be classed as: * `squeeze_simps` * Give inheritance `subX_class S M` → `X s` (where `s : S`) a lower prority than `Y s` → `X s` so that `subY_class S M` → `Y s` → `X s` is preferred over `subY_class S M` → `subX_class S M` → `X s`. This addresses slow unifications when `x : s`, `s` is a submonoid of `t`, which is itself a subgroup of `G`: existing code expects to go `subgroup → group → monoid`, which got changed to `subgroup_class → submonoid_class → monoid`; when this kind of unification issue appears in your type this results in slow unification. By tweaking the priorities, we help the elaborator find our preferred instance, avoiding the big defeq checks. (The real fix should of course be to fix the unifier so it doesn't become exponential in these kinds of cases.) * Split a long proof with duplication into smaller parts. This was basically my last resort. I decided to bump the limit for the `fails_quickly` linter for `measure_theory.Lp_meas.complete_space`, which apparently just barely goes over this limit now. The time difference was about 10%-20% for that specific instance. Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com> Co-authored-by: Riccardo Brasca <riccardo.brasca@gmail.com>
…ascript the coercion between subalgebra and submodule)
25cbe4a
to
e764eef
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really cool! Thank you so much for all the effort you poured into this.
bors merge
This subobject class refactor PR follows up from #11750 by moving the `{zero,one,add,mul,...}_mem_class` lemmas to the root namespace and marking the previous `sub{monoid,group,module,algebra,...}.{zero,one,add,mul,...}_mem` lemmas as `protected`. This is the second part of #11545 to be split off. I made the subobject parameter to the `_mem` lemmas implicit if it appears in the hypotheses, e.g. ```lean lemma mul_mem {S M : Type*} [monoid M] [set_like S M] [submonoid_class S M] {s : S} {x y : M} (hx : x ∈ s) (hy : y ∈ s) : x * y ∈ s ``` instead of having `(s : S)` explicit. The generic `_mem` lemmas are not namespaced, so there is no dot notation that requires `s` to be explicit. Also there were already a couple of instances for the same operator where `s` was implicit and others where `s` was explicit, so some change was needed.
Build failed (retrying...): |
This subobject class refactor PR follows up from #11750 by moving the `{zero,one,add,mul,...}_mem_class` lemmas to the root namespace and marking the previous `sub{monoid,group,module,algebra,...}.{zero,one,add,mul,...}_mem` lemmas as `protected`. This is the second part of #11545 to be split off. I made the subobject parameter to the `_mem` lemmas implicit if it appears in the hypotheses, e.g. ```lean lemma mul_mem {S M : Type*} [monoid M] [set_like S M] [submonoid_class S M] {s : S} {x y : M} (hx : x ∈ s) (hy : y ∈ s) : x * y ∈ s ``` instead of having `(s : S)` explicit. The generic `_mem` lemmas are not namespaced, so there is no dot notation that requires `s` to be explicit. Also there were already a couple of instances for the same operator where `s` was implicit and others where `s` was explicit, so some change was needed.
This PR was included in a batch that was canceled, it will be automatically retried |
This subobject class refactor PR follows up from #11750 by moving the `{zero,one,add,mul,...}_mem_class` lemmas to the root namespace and marking the previous `sub{monoid,group,module,algebra,...}.{zero,one,add,mul,...}_mem` lemmas as `protected`. This is the second part of #11545 to be split off. I made the subobject parameter to the `_mem` lemmas implicit if it appears in the hypotheses, e.g. ```lean lemma mul_mem {S M : Type*} [monoid M] [set_like S M] [submonoid_class S M] {s : S} {x y : M} (hx : x ∈ s) (hy : y ∈ s) : x * y ∈ s ``` instead of having `(s : S)` explicit. The generic `_mem` lemmas are not namespaced, so there is no dot notation that requires `s` to be explicit. Also there were already a couple of instances for the same operator where `s` was implicit and others where `s` was explicit, so some change was needed.
Build failed (retrying...): |
This subobject class refactor PR follows up from #11750 by moving the `{zero,one,add,mul,...}_mem_class` lemmas to the root namespace and marking the previous `sub{monoid,group,module,algebra,...}.{zero,one,add,mul,...}_mem` lemmas as `protected`. This is the second part of #11545 to be split off. I made the subobject parameter to the `_mem` lemmas implicit if it appears in the hypotheses, e.g. ```lean lemma mul_mem {S M : Type*} [monoid M] [set_like S M] [submonoid_class S M] {s : S} {x y : M} (hx : x ∈ s) (hy : y ∈ s) : x * y ∈ s ``` instead of having `(s : S)` explicit. The generic `_mem` lemmas are not namespaced, so there is no dot notation that requires `s` to be explicit. Also there were already a couple of instances for the same operator where `s` was implicit and others where `s` was explicit, so some change was needed.
Pull request successfully merged into master. Build succeeded: |
subobject_class
lemmassubobject_class
lemmas
This subobject class refactor PR follows up from #11750 by moving the
{zero,one,add,mul,...}_mem_class
lemmas to the root namespace and marking the previoussub{monoid,group,module,algebra,...}.{zero,one,add,mul,...}_mem
lemmas asprotected
. This is the second part of #11545 to be split off.I made the subobject parameter to the
_mem
lemmas implicit if it appears in the hypotheses, e.g.instead of having
(s : S)
explicit. The generic_mem
lemmas are not namespaced, so there is no dot notation that requiress
to be explicit. Also there were already a couple of instances for the same operator wheres
was implicit and others wheres
was explicit, so some change was needed.