Zulip Chat Archive

Stream: general

Topic: div_inv_monoid


Yaël Dillies (Apr 09 2022 at 12:02):

I feel like div_inv_monoid does not quite fulfill its purpose of common ancestor of group and group_with_zero because div and inv are currently lawless. We still have a fair share of lemmas which are duplicated between both. For example,

What do people think of making div_inv_monoid stronger and thus potentially more useful for deduplicating lemmas?

Eric Wieser (Apr 09 2022 at 12:22):

The original motivation was div_eq_mul_inv, so it's not entirely lawless

Yaël Dillies (Apr 17 2022 at 14:26):

Okay so I will make div_inv_monoid extend has_involutive_inv. While I'm at it, shouldn't we have div_inv_comm_monoid?

Yaël Dillies (Apr 17 2022 at 14:27):

I am thinking of lemmas like docs#div_eq_inv_mul

Yaël Dillies (Apr 17 2022 at 14:31):

Let me also add another motivation to my original message: Pointwise operations (set, finset, filter) make a group except for a / a = 1. This means that we currently

  • duplicate the instances: docs#set.div_inv_monoid, docs#set.div_inv_monoid' (same for the other two). This is rather minor
  • lack almost all lemmas relating division and inversion. For example, when s t : finset α and α is a group, it is true that (t / s)⁻¹ = s / t but it is a pain to prove.

Yaël Dillies (Apr 17 2022 at 14:33):

div_inv_monoid/div_inv_comm_monoid could help here by being "a group (with zero), except that division doesn't have to bring you back to 1".

Yaël Dillies (Apr 17 2022 at 14:33):

I went through all instances of div_inv_monoid and it seems that nothing relies on the current precise implementation.

Eric Wieser (Apr 17 2022 at 14:41):

I agree that div_eq_inv_mul and similar lemmas look like sufficient justification for div_inv_comm_monoid

Eric Wieser (Apr 17 2022 at 14:41):

Do we already have the group versions of those lemmas?

Yaël Dillies (Apr 17 2022 at 15:28):

docs#div_eq_inv_mul' ?

Yaël Dillies (Apr 17 2022 at 22:18):

Some thoughts over at branch#stronger_div_inv_monoid. I am still figuring whichgroup /group_with_zero lemmas should be turned into axioms of div_inv_monoid. So far I've gone with inv_inv : a⁻¹⁻¹ = a and inv_mul_rev : (a * b)⁻¹ = b⁻¹ * a⁻¹` and it seems to work well.

Yaël Dillies (Apr 18 2022 at 07:47):

Okay I'm hitting a problem with docs#matrix.div_inv_monoid. Inversion of matrices isn't involutive.

Yaël Dillies (Apr 18 2022 at 07:56):

@Yakov Pechersky, is there any way to make this work?

Eric Wieser (Apr 18 2022 at 07:57):

I'd encourage you to do the div_inv_comm_monoid change first before going too deep into this second can of worms

Yaël Dillies (Apr 18 2022 at 07:58):

I mean, are you happy with me nuking matrix.div_inv_monoid into pieces?

Yaël Dillies (Apr 18 2022 at 07:59):

Given that data.matrix.zpow is a leaf file, this should be fine.

Eric Wieser (Apr 18 2022 at 08:01):

It might be worth waiting for @Anne Baanen's opinion on that kind of thing; originally div_inv_monoid was intended purely to unify the two notations, but with your suggested change it's now not able to do that any more (in this case, for matrices)

Yaël Dillies (Apr 18 2022 at 08:02):

Maybe the solution is to have both div_inv_class (for the notation) and div_inv_monoid (for the lemma deduplication).

Eric Wieser (Apr 18 2022 at 08:04):

I'm not sure about the names, but an additional typeclass would alleviate my specific concern above.

Yaël Dillies (Apr 18 2022 at 08:06):

If you look at branch#stronger_div_inv_monoid, you will also see that I had to manually copy the new fields from div_inv_monoid to a bunch of structures because extending div_inv_monoid would make them have many two redundant fields. Having this notation typeclass would fix this.

Yaël Dillies (Apr 18 2022 at 08:06):

What about has_lawful_div?

Eric Wieser (Apr 18 2022 at 08:14):

For the stronger or weaker notion?

Yaël Dillies (Apr 18 2022 at 08:15):

The weaker one.

Yakov Pechersky (Apr 18 2022 at 08:22):

Involution of inverse is too strong for matrices. But as we have it defined, inv thrice is inv.

Yakov Pechersky (Apr 18 2022 at 08:23):

Ask Anne, they made the matrix inv work. The zpow is just making its commutation make sense with npow.

Yakov Pechersky (Apr 18 2022 at 08:36):

I think the div_eq_inv_mul is a powerful law, because it implies statements about associativity of various operators. The fact that the matrix semiring admits an inverse that respects this structure is to me, reason enough to have it.

Yaël Dillies (Apr 18 2022 at 08:46):

It doesn't buy you much, though. You can take an arbitrary inv and derive div from it. Sure, we do get a few lemmas about the interaction of div and mul but that's it.

Yaël Dillies (Apr 18 2022 at 08:46):

matrix actually respects docs#matrix.mul_inv_rev, which is a pretty crucial lemma.

Yaël Dillies (Apr 18 2022 at 09:03):

Just look at the sheer amount of lemmas I can deduplicate/generalize and convince yourself: branch#stronger_div_inv_monoid

Eric Wieser (Apr 18 2022 at 10:10):

Removing an existing typeclass is an orthogonal discussion to adding a new one

Eric Wieser (Apr 18 2022 at 10:11):

(making a typeclass stronger to the point that existing instances are no longer true amounts to removing it and adding a new one with the same name)

Yaël Dillies (Apr 18 2022 at 13:34):

Okay so I have three propositions, modulo naming:

  • Have a class which just contains the bare minimum for division and inversion and use it in extends clause to avoid copying over all the zpow fields, and add a stronger typeclass
    • div_inv_class = the current div_inv_monoid
    • div_inv_monoid = div_inv_class + has_involutive_inv
  • Moderately strengthen div_inv_monoid and add a stronger typeclass
    • div_inv_class = the current div_inv_monoid + (a * b)⁻¹ = b⁻¹ * a⁻¹
    • div_inv_monoid = div_inv_class + has_involutive_inv. I did not yet manage to prove a * b = 1 → a⁻¹ = b from this, even though all candidate types (yes, even set) in mathlib respect it. If it doesn't hold generally, we might consider adding it as well.
  • Strengthen div_inv_monoid and make the weaker one a mixin typeclass
    • div_inv_class = mixin for a / b = a * b⁻¹ and eventually the zpow fields. Does that work diamond-wise?
    • div_inv_monoid = the current div_inv_monoid + has_involutive_inv.

All three versions do not break any current instance of div_inv_monoid, and actually only one of them (namely docs#matrix.div_inv_monoid) can't be upgraded to the new version of div_inv_monoid.

Yaël Dillies (Apr 18 2022 at 13:41):

Another possibility for the names is div_inv_monoid and division_monoid.

Eric Wieser (Apr 18 2022 at 13:47):

Another name that may or may not be useful is has_distrib_inv to somewhat match docs#has_distrib_neg (although the type of distributivity is quite different)

Reid Barton (Apr 18 2022 at 13:52):

I wouldn't regard these as similar, distributivity is about the interaction of multiplication and addition, but here there is only multiplicative structure. You can see also the form of the axiom neg_mul is different from (a * b)⁻¹ = b⁻¹ * a⁻¹ because in neg_mul only one side is negated. It is like g.xy=(g.x)yg.xy = (g.x)y versus g.xy=(g.x)(g.y)g.xy = (g.x)(g.y) again.

Yaël Dillies (Apr 18 2022 at 13:53):

It's rather left or right commutativity of a unary and a binary functions.

Yaël Dillies (Apr 18 2022 at 13:54):

... which means that has_distrib_neg might be a misnomer.

Reid Barton (Apr 18 2022 at 13:56):

The name has_distrib_neg seems fine to me, it's the part of distributivity/bilinearity that has to do with negation in each argument.

Reid Barton (Apr 18 2022 at 13:59):

I mean it's not neg itself that is distributive, but otherwise it's fine.

Reid Barton (Apr 18 2022 at 14:01):

Oh it depends on whether you think of neg as part of the additive structure or as multiplication by 1-1; maybe that's what you meant?

Yaël Dillies (Apr 18 2022 at 14:01):

Yes, kind of

Reid Barton (Apr 18 2022 at 14:04):

Like (a+b)=(1)(a+b)=(1)a+(1)b=ab-(a + b) = (-1) * (a + b) = (-1) * a + (-1) * b = - a - b which now looks like div_inv stuff and not neg_mul yet I used the distributive law to get it

Eric Wieser (Apr 18 2022 at 14:23):

has_distrib_inv was a dumb suggestion because we need a name for the additive concept -(a + b) = -b + -a, sorry for the distraction.

Eric Wieser (Apr 18 2022 at 14:23):

And the additive name is obviously already taken

Yaël Dillies (Apr 18 2022 at 14:24):

Technically, it's not distributivity, but antidistributivity.

Yaël Dillies (Apr 18 2022 at 15:32):

I'm hitting something weird... Making docs#linear_ordered_add_comm_group_with_top extend has_sub and has_neg instead of sub_neg_monoid and copying over the fields makes Lean reject the default value for sub.

Yaël Dillies (Apr 21 2022 at 07:05):

Okay so I managed to make version 1 compile. The diff is rather huge, so I will first PR the definition of division_monoid, then the lemma generalizations.

Anne Baanen (Apr 21 2022 at 08:40):

It's been a while, hopefully my comments don't lead to too much wasted work!

The definition of div_inv_monoid had the following main motivation: group_with_zero used to assume div would be defined in terms of inv but for fractional_ideal it's the other way around. So we should replace the definitional equality a / b = a * b⁻¹ with a propositional one. Since group might have the same issue in the future, I made a superclass of both group and group_with_zero div_inv_monoid carrying a definition of div and inv and a proof div_eq_mul_inv. A few results nicely generalized from group(_with_zero) to div_inv_monoid but the motivation was to remove a technical obstruction to a specific instance, rather than generalizing as many lemmas as possible.

Having said that, I think it's a valuable project to generalize as much as reasonable, so indeed a stronger subclass of group and group_with_zero does make sense.

Yaël Dillies (Apr 21 2022 at 08:42):

If you could have a look at branch#stronger_div_inv_monoid and tell me what you think, I would be grateful!

Anne Baanen (Apr 21 2022 at 09:10):

Alright, here's my thoughts on the implementation:

  • Mixins can be useful in specific cases, but they do have a tendency to hurt performance. In particular, the fully unfolded term size of a type determines the worst-case typechecking behaviour. So a long inheritance chain including mixins on a group G instance can really hurt typechecking on s : submonoid G, and even worse on typechecking x : s. Also, we probably don't need to switch often in applications between assuming that inverses are very lawful and only a bit lawful, so mixins probably don't help either.
  • Naïvely I would assume "monoid with a (partial) inverse operator" is one with a strong specification on the inverse of inv_spec : ∀ a b, a * b = 1 → a⁻¹ = b. As far as I can tell, your code puts that in group still, and in the thread you noted that everything currently already satisfies this axiom. (An example of a division_monoid in your branch that doesn't satisfy inv_spec would be a comm_monoid where inv = id.)
  • I think we should keep a class isomorphic to div_inv_monoid, since its syntactic role means it can be easily applied to any type with division and inverses, even if those inverses aren't neatly involutive (like matrix (pseudo)inverses).

Anne Baanen (Apr 21 2022 at 09:14):

So overall I think your branch is going in the right direction, except I would like the inv_spec axiom to be a part of the new class. The name division_monoid I'm not totally convinced of (since mathematicians tend to care more about inverses than division) but I guess it works since it's essentially the meet of group and division_ring.

Yaël Dillies (Apr 21 2022 at 09:18):

  • division_monoid is not a mixin, though?
  • If you think this axiom is justified, I will add it. The question now is whether this implies any of inv_mul_rev : ∀ a b, (a * b)⁻¹ = b⁻¹ * a⁻¹ or inv_inv : ∀ a, a⁻¹⁻¹ = a. I believe matrix is a counterexample for inv_inv, and I can't see how we could derive inv_mul_rev because we would need some cancellation property to prove (a * b) * (b⁻¹ * a⁻¹) = 1. One thing I also have to check is whether filter respects inv_spec.
  • Does that mean you do not want to have both div_inv_monoid and division_monoid? because this is a problem for classes currently extending div_inv_monoid as we do not want to clutter their declarations with the redundant inv_mul_rev and inv_inv.
  • I also thought about inversion_monoid, but I quite like the parallel with division_ring indeed.

Anne Baanen (Apr 21 2022 at 09:25):

  • Indeed! I was trying to respond to some questions on mixins upthread.
  • I agree that neither follows from inv_spec, for example if you take the naturals then neg_spec only says - 0 = 0 and leaves neg totally unspecified otherwise. But if filter also satisfies inv_spec then I don't see that adding it as a new axiom (alongside inv_inv and inv_mul_rev) will cause any problems, while providing a better base for the group axioms.
  • I wanted to say that keeping div_inv_monoid as it is, like you have done, seems like the correct approach to me. Sorry if that was unclear.

Yaël Dillies (Apr 21 2022 at 09:26):

Fancy! I will figure out the filter conundrum and add inv_spec then! (do you think there's a better name for it?)

Anne Baanen (Apr 21 2022 at 09:27):

For groups it's called docs#eq_inv_of_mul_eq_one so let's use that.

Yaël Dillies (Apr 21 2022 at 17:15):

Okay so I managed to prove that inv_eq_of_mul_eq_one on α implies inv_eq_of_mul_eq_one on set α/finset α, but it was harder than I thought! When multiplication is cancellable, you can show that s and t are singletons and then s⁻¹ = t is pretty obvious, but of course a group_with_zero isn't cancellative.

example (h : s * t = 1) : s⁻¹ = t :=
begin
  ext a,
  rw mem_inv,
  have H :  {a b}, a  s  b  t  a⁻¹ = b :=
    λ a b ha hb, inv_eq_of_mul_eq_one (h.subset $ mem_image2_of_mem ha hb),
  have hst : (s * t).nonempty := h.symm.subst one_nonempty,
  refine λ ha, _, λ ha, _⟩,
  { obtain b, hb := hst.of_image2_right,
    rwa [inv_inv a, H ha hb] },
  { obtain b, hb := hst.of_image2_left,
    rwa [H hb ha, inv_inv] }
end

Floris van Doorn (Apr 22 2022 at 14:01):

But H together with hst still are telling you that s and t are singletons, right?

Yaël Dillies (Apr 22 2022 at 14:03):

Hmm... You're right. I will try proving the earlier lemma without using cancellability.

Yaël Dillies (May 01 2022 at 13:35):

Update: filter is a division_monoid! I will now open the PR.

Yaël Dillies (May 01 2022 at 14:57):

#13860

Yaël Dillies (May 07 2022 at 00:50):

Here's the second bit of the refactor: #14000. It introduces new division_monoid lemmas to replace the group and group_with_zero ones (but leaves the deletion of those to the next PR).

Yaël Dillies (May 07 2022 at 00:51):

I tried my best to make it as straightforward as possible.

Yaël Dillies (Apr 12 2023 at 12:12):

I crunched the numbers for an upcoming paper and found out that division monoids allowed us to generalise 483 lemmas! 293 (manually generated) lemmas mention docs#division_monoid, and 190 mention docs#division_comm_monoid.

Source

Floris van Doorn (Apr 12 2023 at 14:58):

You're counting all declarations - not just lemmas - right? I'm not sure whether declarations like docs#group.to_division_monoid and docs#prod.division_monoid should count towards the usefulness of division monoids.

Yaël Dillies (Apr 12 2023 at 17:26):

The limitations of my metaprogramming skills have been uncovered :grinning_face_with_smiling_eyes: How do I filter out instances?

Floris van Doorn (Apr 12 2023 at 21:53):

An easy check is to use docs#declaration.is_theorem if you only want to count lemmas (I'm quite sure all instances are not theorems, even if they are propositional instances). You can use docs#tactic.is_instance to check for instances.


Last updated: Dec 20 2023 at 11:08 UTC