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

[Merged by Bors] - feat(*): introduce classes for types of homomorphism #9888

Closed
wants to merge 8 commits into from

Conversation

Vierkantor
Copy link
Collaborator

@Vierkantor Vierkantor commented Oct 22, 2021

This PR is the main proof-of-concept in my plan to use typeclasses to reduce duplication surrounding hom classes. Essentially, I want to take each type of bundled homs, such as monoid_hom, and add a class monoid_hom_class which has an instance for each type extending monoid_hom. Declarations that now take a parameter of the specific type monoid_hom M N can instead take a more general {F : Type*} [monoid_hom_class F M N] (f : F); this means we don't need to duplicate e.g. monoid_hom.map_prod to ring_hom.map_prod, mul_equiv.map_prod, ring_equiv.map_prod, or monoid_hom.map_div to ring_hom.map_div, mul_equiv.map_div, ring_equiv.map_div, ...

Basically, instead of having O(n * k) declarations for n types of homs and k lemmas, following the plan we only need O(n + k).

Overview

API changes

Lemmas matching *_hom.map_{add,zero,mul,one,sub,div} are deprecated. Use the new simp lemmas called simply map_add, map_zero, ...

Namespaced lemmas of the form map_{add,zero,mul,one,sub,div} are now protected. This includes e.g. polynomial.map_add and multiset.map_add, which involve polynomial.map and multiset.map respectively. In fact, it should be possible to turn those map definitions into bundled maps, so we don't even need to worry about the name change.

New classes

  • zero_hom_class, one_hom_class defines map_zero, map_one
  • add_hom_class, mul_hom_class defines map_add, map_mul
  • add_monoid_hom_class, monoid_hom_class extends {zero,one}_hom_class, {add,mul}_hom_class
  • monoid_with_zero_hom_class extends monoid_hom_class and zero_hom_class
  • ring_hom_class extends monoid_hom_class, add_monoid_hom_class and monoid_with_zero_hom_class

Classes still to be implemented

Some of the core algebraic homomorphisms are still missing their corresponding classes:

  • mul_action_hom_class defines map_smul
  • distrib_mul_action_hom_class, mul_semiring_action_hom_class extends the above
  • linear_map_class extends add_hom_class and defines map_smulₛₗ
  • alg_hom_class extends ring_hom_class and defines commutes

We could also add an equiv_like and its descendants add_equiv_class, mul_equiv_class, ring_equiv_class, linear_equiv_class, ...

Other changes

coe_fn_coe_base now has an appropriately low priority, so it doesn't take precedence over fun_like.has_coe_to_fun.

Why are you unbundling the morphisms again?

It's not quite the same thing as unbundling. When using unbundled morphisms, parameters have the form (f : A → B) (hf : is_foo_hom f); bundled morphisms look like (f : foo_hom A B) (where foo_hom A B is equivalent to { f : A → B // is_foo_hom f }; typically you would use a custom structure instead of subtype). This plan puts a predicate on the type foo_hom rather than the elements of the type as you would with unbundled morphisms. I believe this will preserve the advantages of the bundled approach (being able to talk about the identity map, making it work with simp), while addressing one of its disadvantages (needing to duplicate all the lemmas whenever extending the type of morphisms).

Discussion

Main Zulip thread: https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Typeclasses.20for.20morphisms

Some other threads referencing this plan:


Open in Gitpod

@Vierkantor Vierkantor added WIP Work in progress RFC Request for comment labels Oct 22, 2021
@github-actions github-actions bot added merge-conflict Please `git merge origin/master` then a bot will remove this label. and removed merge-conflict Please `git merge origin/master` then a bot will remove this label. labels Nov 5, 2021
@Vierkantor Vierkantor removed the WIP Work in progress label Nov 11, 2021
@urkud
Copy link
Member

urkud commented Nov 11, 2021

Would it be hard to split fun_like from the diff? If not, could you please PR it separately so that we can have it before the whole PR passes the review? If it's hard, then ignore this comment.

@Vierkantor
Copy link
Collaborator Author

Vierkantor commented Nov 11, 2021

Sure, I can just copy the commit introducing fun_like.lean to its own PR.

@github-actions github-actions bot added blocked-by-other-PR This PR depends on another PR which is still in the queue. A bot manages this label via PR comment. merge-conflict Please `git merge origin/master` then a bot will remove this label. labels Nov 11, 2021
@github-actions github-actions bot removed the merge-conflict Please `git merge origin/master` then a bot will remove this label. label Nov 16, 2021
bors bot pushed a commit that referenced this pull request Nov 22, 2021
…ofs) (#10286)

This PR introduces a class `fun_like` for types of bundled homomorphisms, like `set_like` is for bundled subobjects. This should be useful by itself, but an important use I see for it is the per-morphism class refactor, see #9888.

Also, `coe_fn_coe_base` now has an appropriately low priority, so it doesn't take precedence over `fun_like.has_coe_to_fun`.



Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com>
@github-actions github-actions bot added merge-conflict Please `git merge origin/master` then a bot will remove this label. and removed blocked-by-other-PR This PR depends on another PR which is still in the queue. A bot manages this label via PR comment. labels Nov 22, 2021
@Vierkantor Vierkantor changed the title WIP: feat(*): introduce classes for types of homomorphism feat(*): introduce classes for types of homomorphism Nov 24, 2021
@Vierkantor Vierkantor added the awaiting-review The author would like community review of the PR label Nov 24, 2021
@github-actions github-actions bot removed the merge-conflict Please `git merge origin/master` then a bot will remove this label. label Nov 24, 2021
ericrbg pushed a commit that referenced this pull request Nov 24, 2021
…ofs) (#10286)

This PR introduces a class `fun_like` for types of bundled homomorphisms, like `set_like` is for bundled subobjects. This should be useful by itself, but an important use I see for it is the per-morphism class refactor, see #9888.

Also, `coe_fn_coe_base` now has an appropriately low priority, so it doesn't take precedence over `fun_like.has_coe_to_fun`.



Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com>
@eric-wieser
Copy link
Member

Namespaced lemmas of the form map_{add,zero,mul,one,sub,div} are now protected. This includes e.g. polynomial.map_add and multiset.map_add, which involve polynomial.map and multiset.map respectively.

This might be worth splitting into a separate PR, if doing so is easy and this hits lots of otherwise untouched files.

Comment on lines 359 to 389
instance : add_monoid_hom_class (α →+* β) α β :=
{ coe := ring_hom.to_fun,
coe_injective' := λ f g h, by cases f; cases g; congr',
map_add := ring_hom.map_add',
map_zero := ring_hom.map_zero' }

instance : monoid_hom_class (α →+* β) α β :=
{ coe := ring_hom.to_fun,
coe_injective' := λ f g h, by cases f; cases g; congr',
map_mul := ring_hom.map_mul',
map_one := ring_hom.map_one' }
Copy link
Member

Choose a reason for hiding this comment

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

Would it be better here to go straight to adding a ring_hom_class? Or would you argue there is no point in such a class, and using these two classes separately is easier?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

With the current design we'd need a separate ring_hom_class, since taking the add_monoid_hom_class and monoid_hom_class in separate parameters can have different to_funs.

An alternative is to unbundle the fun_like from the _hom_classes, taking it as an instance parameter rather than extending it:

class zero_hom_class (F : Type*) (M N : out_param $ Type*) [fun_like F M (λ _, N)]
  [has_zero M] [has_zero N] :=
(map_zero : ∀ (f : F), f 0 = 0)

Then we wouldn't need to define the conjunction of the _hom_classes separately, just one per operation. The disadvantage being that the list of instance parameters becomes extremely long:

theorem polynomial.hom_eval₂ {R S T : Type*} [semiring R] (p : polynomial R)
  [comm_semiring S] [comm_semiring T]
  (f : R →+* S) (g : S →+* T) (x : S) :
  ⇑g (polynomial.eval₂ f x p) = polynomial.eval₂ (g.comp f) (⇑g x) p

becomes

theorem polynomial.hom_eval₂ {R S T F G : Type*} [semiring R] (p : polynomial R)
  [comm_semiring S] [comm_semiring T]
  [fun_like F R (λ _, S)] [add_hom_class F R S] [mul_hom_class F R S] [one_hom_class F R S] [zero_hom_class F R S]
  [fun_like G S (λ _, T)] [add_hom_class G S T] [mul_hom_class G S T] [one_hom_class G S T] [zero_hom_class G S T]
  (f : F) (g : G) (x : S) :
  ⇑g (polynomial.eval₂ f x p) = polynomial.eval₂ (g.comp f) (⇑g x) p

instead of my preferred bundled design:

theorem polynomial.hom_eval₂ {R S T F G : Type*} [semiring R] (p : polynomial R)
  [comm_semiring S] [comm_semiring T] [ring_hom_class F R S] [ring_hom_class G S T]
  (f : F) (g : G) (x : S) :
  ⇑g (polynomial.eval₂ f x p) = polynomial.eval₂ (g.comp f) (⇑g x) p

Copy link
Member

Choose a reason for hiding this comment

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

Note that if we just want to shrink the argument list, we can still have a vestigial ring_hom_class that extends the others and use [fun_like G S (λ _, T)] [ring_hom_class G S T]; which maybe has an advantage since there are no longer any diamonds in the data-carrying classes. I think your design is fine though.

In that case, can you replace these two instances with class ring_hom_class extends ... and a single instance : ring_hom_class?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Indeed, I think we'll end up having to unbundle parts of the linear/mul_action hom classes anyway: if it was a subclass of fun_like we'd get a dangerous instance linear_map_class F σ M N → fun_like F M N. But since they're also add_homs, the class would look something more like:

class linear_map_class (F : Type*) {R S : Type*} [semiring R] [semiring S] (σ : R →+* S) (M M₂ : Type*)
  [add_comm_monoid M] [add_comm_monoid M₂] [module R M] [module S M₂]
  [add_hom_class F M M₂] := -- alternatively [fun_like F M (λ _, M₂)] [add_hom_class F M M₂] :=
(map_smul' : ∀ (f : F) (r : R) (x : M), f (r • x) = (σ r) • f x)

so unbundling fun_like from the add_hom_class doesn't really bring anything in that case.

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've added a commit defining a ring_hom_class. I expect nothing will break, let's see :P

@Vierkantor
Copy link
Collaborator Author

Vierkantor commented Dec 2, 2021

Namespaced lemmas of the form map_{add,zero,mul,one,sub,div} are now protected. This includes e.g. polynomial.map_add and multiset.map_add, which involve polynomial.map and multiset.map respectively.

This might be worth splitting into a separate PR, if doing so is easy and this hits lots of otherwise untouched files.

I'm currently compiling a branch (edit: #10580) with commits d0a17c6 and c4a31b2 cherry-picked. Let's see how much manual fixing it needs...

@github-actions
Copy link

github-actions bot commented Dec 3, 2021

🎉 Great news! Looks like all the dependencies have been resolved:

💡 To add or remove a dependency please update this issue/PR description.

Brought to you by Dependent Issues (:robot: ). Happy coding!

@github-actions github-actions bot removed the merge-conflict Please `git merge origin/master` then a bot will remove this label. label Dec 3, 2021
Copy link
Collaborator

@sgouezel sgouezel left a comment

Choose a reason for hiding this comment

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

I absolutely love this idea. Two questions:

  • could you explain this design idea somewhere (probably in some library note -- then one could refer to it when defining one of these helper classes)? Mentioning in particular the O(n * k) against O(n) + O(k) issue
  • in the current version, it is rather O(n * k) + O(n) + O(k), as you don't remove any of the preexisting lemmas. Is that in your plans later?

src/algebra/module/linear_map.lean Outdated Show resolved Hide resolved
@Vierkantor
Copy link
Collaborator Author

  • in the current version, it is rather O(n * k) + O(n) + O(k), as you don't remove any of the preexisting lemmas. Is that in your plans later?

Yes, I tried doing all of this in one go and it extremely quickly got out of hand. So this will happen in a number of separate follow-ups.

@Vierkantor
Copy link
Collaborator Author

could you explain this design idea somewhere (probably in some library note -- then one could refer to it when defining one of these helper classes)? Mentioning in particular the O(n * k) against O(n) + O(k) issue

Almost forgot to respond to this one. I wrote this in the module documentation for fun_like, would you like to have this instead as a library note named something like "morphism classes"?

Copy link
Member

@jcommelin jcommelin left a comment

Choose a reason for hiding this comment

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

Thanks 🎉

bors merge


/-- Group homomorphisms preserve inverse. -/
@[simp, to_additive]
theorem map_inv [group G] [group H] [monoid_hom_class F G H]
Copy link
Member

@jcommelin jcommelin Dec 10, 2021

Choose a reason for hiding this comment

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

Should this now in fact be proven for mul_hom_class? Because that's all you need. (Shouldn't hold up this PR. Can be done later.)

@github-actions github-actions bot added ready-to-merge All that is left is for bors to build and merge this PR. (Remember you need to say `bors r+`.) and removed awaiting-review The author would like community review of the PR labels Dec 10, 2021
bors bot pushed a commit that referenced this pull request Dec 10, 2021
This PR is the main proof-of-concept in my plan to use typeclasses to reduce duplication surrounding `hom` classes. Essentially, I want to take each type of bundled homs, such as `monoid_hom`, and add a class `monoid_hom_class` which has an instance for each *type* extending `monoid_hom`. Declarations that now take a parameter of the specific type `monoid_hom M N` can instead take a more general `{F : Type*} [monoid_hom_class F M N] (f : F)`; this means we don't need to duplicate e.g. `monoid_hom.map_prod` to `ring_hom.map_prod`, `mul_equiv.map_prod`, `ring_equiv.map_prod`, or `monoid_hom.map_div` to `ring_hom.map_div`, `mul_equiv.map_div`, `ring_equiv.map_div`, ...

Basically, instead of having `O(n * k)` declarations for `n` types of homs and `k` lemmas, following the plan we only need `O(n + k)`.

## Overview

 * Change `has_coe_to_fun` to include the type of the function as a parameter (rather than a field of the structure) (**done** as part of #7033)
 * Define a class `fun_like`, analogous to `set_like`, for types of bundled function + proof (**done** in #10286)
 * Extend `fun_like` for each `foo_hom` to create a `foo_hom_class` (**done** in this PR for `ring_hom` and its ancestors, **todo** in follow-up for the rest)
 * Change parameters of type `foo_hom A B` to take `{F : Type*} [foo_hom_class F A B] (f : F)` instead (**done** in this PR for `map_{add,zero,mul,one,sub,div}`, **todo** in follow-up for remaining declarations)

## API changes

Lemmas matching `*_hom.map_{add,zero,mul,one,sub,div}` are deprecated. Use the new `simp` lemmas called simply `map_add`, `map_zero`, ...

Namespaced lemmas of the form `map_{add,zero,mul,one,sub,div}` are now protected. This includes e.g. `polynomial.map_add` and `multiset.map_add`, which involve `polynomial.map` and `multiset.map` respectively. In fact, it should be possible to turn those `map` definitions into bundled maps, so we don't even need to worry about the name change.

## New classes

 * `zero_hom_class`, `one_hom_class` defines `map_zero`, `map_one`
 * `add_hom_class`, `mul_hom_class` defines `map_add`, `map_mul`
 * `add_monoid_hom_class`, `monoid_hom_class` extends `{zero,one}_hom_class`, `{add,mul}_hom_class`
 * `monoid_with_zero_hom_class` extends `monoid_hom_class` and `zero_hom_class`
 * `ring_hom_class` extends `monoid_hom_class`, `add_monoid_hom_class` and `monoid_with_zero_hom_class`

## Classes still to be implemented

Some of the core algebraic homomorphisms are still missing their corresponding classes:

 * `mul_action_hom_class` defines `map_smul`
 * `distrib_mul_action_hom_class`, `mul_semiring_action_hom_class` extends the above
 * `linear_map_class` extends `add_hom_class` and defines `map_smulₛₗ`
 * `alg_hom_class` extends `ring_hom_class` and defines `commutes`

We could also add an `equiv_like` and its descendants `add_equiv_class`, `mul_equiv_class`, `ring_equiv_class`, `linear_equiv_class`, ...

## Other changes

`coe_fn_coe_base` now has an appropriately low priority, so it doesn't take precedence over `fun_like.has_coe_to_fun`.

## Why are you unbundling the morphisms again?

It's not quite the same thing as unbundling. When using unbundled morphisms, parameters have the form `(f : A → B) (hf : is_foo_hom f)`; bundled morphisms look like `(f : foo_hom A B)` (where `foo_hom A B` is equivalent to `{ f : A → B // is_foo_hom f }`; typically you would use a custom structure instead of `subtype`). This plan puts a predicate on the *type* `foo_hom` rather than the *elements* of the type as you would with unbundled morphisms. I believe this will preserve the advantages of the bundled approach (being able to talk about the identity map, making it work with `simp`), while addressing one of its disadvantages (needing to duplicate all the lemmas whenever extending the type of morphisms).

## Discussion

Main Zulip thread: https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Typeclasses.20for.20morphisms

Some other threads referencing this plan:
 * https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/Morphism.20refactor
 * https://leanprover.zulipchat.com/#narrow/stream/263328-triage/topic/issue.20.231044.3A.20bundling.20morphisms
 * #1044
 * #4985



Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com>
@bors
Copy link

bors bot commented Dec 10, 2021

Pull request successfully merged into master.

Build succeeded:

@bors bors bot changed the title feat(*): introduce classes for types of homomorphism [Merged by Bors] - feat(*): introduce classes for types of homomorphism Dec 10, 2021
@bors bors bot closed this Dec 10, 2021
@bors bors bot deleted the fun_like branch December 10, 2021 15:16
jcommelin pushed a commit that referenced this pull request Dec 18, 2021
This PR prepares for #9888 by namespacing all usages of `to_fun` and `map_{add,zero,mul,one,div}` that do not involve the classes of #9888. This includes e.g. `polynomial.map_add` and `multiset.map_add`, which involve `polynomial.map` and `multiset.map` respectively.
bors bot pushed a commit that referenced this pull request Dec 18, 2021
…g maps (#10755)

Use the design of #9888 to define a class `rel_hom_class F r s` for types of maps such that all `f : F` satisfy `r a b → s (f a) (f b)`. Requested by @YaelDillies.

`order_hom_class F α β` is defined as an abbreviation for `rel_hom_class F (≤) (≤)`.
bors bot pushed a commit that referenced this pull request Jan 31, 2022
This PR defines a class of types of multiplicative (additive) equivalences, along the lines of #9888.



Co-authored-by: Anne Baanen <Vierkantor@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready-to-merge All that is left is for bors to build and merge this PR. (Remember you need to say `bors r+`.) RFC Request for comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants