Zulip Chat Archive
Stream: mathlib4
Topic: to_additive on AddMonoidWithOne
Chris Birkbeck (Aug 01 2025 at 08:56):
I'm having trouble with the following to_additive, which I think should work since I can prove the additive version with the same proof (as seen below):
@[to_additive]
theorem pnat_multipliable_iff_multipliable_succ' {α R : Type*} [AddMonoidWithOne R]
[TopologicalSpace α] [CommMonoid α] {f : R → α} :
Multipliable (fun x : ℕ+ => f x) ↔ Multipliable fun x : ℕ => f (x + 1) := by
convert Equiv.pnatEquivNat.symm.multipliable_iff.symm
simp
theorem pnat_summable_iff_summable_succ' {α R : Type*} [AddMonoidWithOne R]
[TopologicalSpace α] [AddCommMonoid α]
{f : R → α} : Summable (fun x : ℕ+ => f x) ↔ Summable fun x : ℕ => f (x + 1) := by
convert Equiv.pnatEquivNat.symm.summable_iff.symm
simp
The to_additive is giving the following error, which I'm not sure how to fix:
Aplication type mismatch: In the application
@Zero.toOfNat0 R inst✝².toOne
the argument
inst✝².toOne
has type
One R : Type u_11
but is expected to have type
Zero R : Type u_11
Chris Birkbeck (Aug 01 2025 at 17:12):
Aaron Liu (Aug 01 2025 at 17:14):
You don't lose any generality if you take f : ℕ → α instead
Aaron Liu (Aug 01 2025 at 17:14):
so drop the AddMonoidWithOne
Aaron Liu (Aug 01 2025 at 17:15):
hmm
Aaron Liu (Aug 01 2025 at 17:16):
I guess you end up with f ↑(x + 1) on the rhs instead of f (↑x + 1)
Aaron Liu (Aug 01 2025 at 17:16):
now I'm not sure anymore
Chris Birkbeck (Aug 01 2025 at 17:19):
yeah the coercions become annoying which is why I want this version.
Bryan Gin-ge Chen (Aug 01 2025 at 18:03):
Just from the error message it looks like maybe @Jovan Gerbscheid can explain?to_additive isn't translating One to Zero. I seem to recall there was some issue with those typeclasses,
Weiyi Wang (Aug 01 2025 at 18:05):
or perhaps it is translating 1 to 0 when it shouldn't in this case?
Chris Birkbeck (Aug 01 2025 at 18:08):
Yeah I don't think it should be trying to translate it and there is something in the documentation about this for multiplicative instances, but I can't seem to get it to ignore R when doing this.
Jovan Gerbscheid (Aug 01 2025 at 19:17):
Yes, this is a known limitation of to_additive. The heuristics are smart enough to see that 1 : ℕ should not be translated to 0 : ℕ, but it still tries to translate 1 : R to 0 : R, because it doesn't take into account that the R has an AddMonoidWithOne instance. For example, a consequence of this is that the theory of AddMonoidAlgebra and MonoidAlgebra are duplicated in the same file, instead of using to_additive.
I think there are two ways in which this problem might be solved (in a hypothetical future fix).
- introduce the syntax
@[to_additive (dont_translate := R)to tellto_additiveto not translate multiplicative things relating toR. - in addition to the above, let
to_additiveinfer fromAddMonoidWithOne RthatRshould not be translated. This would involve tagging all instances that are multiplicative and additive, such asRing,FieldandAddMonoidWithOne.
Chris Birkbeck (Aug 01 2025 at 19:50):
@[to_additive (dont_translate := R)] doesnt seem to work, it says: unexpected identifier; expected 'attr' or 'reorder'
Chris Birkbeck (Aug 01 2025 at 19:50):
also how/where do I tag the instances in your second point?
Etienne Marion (Aug 01 2025 at 19:51):
These are potential solutions, but they have not been implemented.
Chris Birkbeck (Aug 01 2025 at 19:54):
oh sorry I misread!
Jovan Gerbscheid (Aug 05 2025 at 23:22):
I've though about this a bit more, and I think we can add the following heuristic:
For any to_additive constant, let the nth argument be the first multiplicative argument (there is already code that looks for this, related to to_additive_relevant_arg). Then when translating this constant, we check if anything in the nth argument has been tranlated. If not, we also don't translate this constant.
In the original example, One.toOfNat was translated to Zero.toOfNat, but its argument inst.toOne was not translated. Hence this heuristic would fix that, so that neither gets translated. Hopefully this will also make MonoidAlgebra work with to_additive.
I won't be able to work on this for a week now so if anyone wants to try, feel free to.
cc @Floris van Doorn
Floris van Doorn (Aug 06 2025 at 10:43):
Jovan Gerbscheid said:
I've though about this a bit more, and I think we can add the following heuristic:
For any to_additive constant, let the nth argument be the first multiplicative argument (there is already code that looks for this, related toto_additive_relevant_arg). Then when translating this constant, we check if anything in the nth argument has been tranlated. If not, we also don't translate this constant.In the original example, One.toOfNat was translated to Zero.toOfNat, but its argument inst.toOne was not translated. Hence this heuristic would fix that, so that neither gets translated. Hopefully this will also make
MonoidAlgebrawork with to_additive.
I don't understand what you mean here. Can you expand it more?
Usually the "first multiplicative argument" is a type argument, which is often a variable, and so often nothing gets translated in this argument.
If you mean "look at the type-class argument(s), and see whether anything in that gets translated", that might be an interesting idea. But the basic thing doesn't work. E.g. if we have something which uses the instance CommMonoid.toMonoid (CommRing.toCommMonoid inst) then to_additive will currently translate this to the (type-incorrect) AddCommMonoid.toAddMonoid (CommRing.toCommMonoid inst), so we also have to block that translation.
This gets complicated, but it might actually work. When we translate foo a1 a2 ... an, we first translate all arguments. Then we take (all?) the type-class arguments on the first multiplicative argument, and see if their translation changed anything. Then we translate foo to its additive version only if
- the current heuristic says we can change it
- the type-class arguments either changed or is a variable.
Note: the recursive call of the translation might have to return a boolean stating "I have changed my subexpression", or more accurately "the above test passed", since for lemmas with to_additive self, the test can pass and we can replace foo with its additive version, without actually changing the term.
Jovan Gerbscheid (Aug 06 2025 at 11:24):
Yes, that is what I meant, looking at the multiplicative type class argument. We could look at multiple type class arguments, but I don't think that would gain us more than just looking at the first one. In the algorithm that determines the to_additive_relevant_arg, it first finds the first argument whose type gets translated (i.e. the type class argument), and then if the first argument of that is a variable, then that is the relevant multiplicative argument.
And indeed a good way to do this is to have the recursive call return a Bool to say whether anything has been tranlated in there. I hadn't thought of the case of the argument being a variable, that case should indeed also be tranlated.
Jovan Gerbscheid (Aug 18 2025 at 14:21):
I've made #28591 that implements the above heuristic, and I've been able to use @[to_additive] on almost everything in Algebra.MonoidAlgebra.Defs :tada:
Chris Birkbeck (Aug 18 2025 at 14:21):
Oh great! thanks so much for doing this!
Jovan Gerbscheid (Aug 19 2025 at 09:31):
I've encountered some limitations of my current approach, so I'm considering to instead do something closer to my original suggestion in this thread. For example:
- Figuring out whether an instance has changed isn't always as easy as it seems. There can be an instance of type
Mul (MonoidAlgebra _ _)which gets translated into an instance of typeMul (AddMonoidAlgebra). My heuristic would pick this up as that it was changed, but since it is still aMulinstance, in should count as unchanged. - Some functions, like
MulOpposite, don't have an instance argument, so my heuristic doesn't work here. - The following instance failed to be translated by my version of
to_additive:
instance isScalarTower [Semiring k] [SMulZeroClass R k] [SMulZeroClass S k] [SMul R S]
[IsScalarTower R S k] : IsScalarTower R S (MonoidAlgebra k G) :=
Finsupp.isScalarTower G k
The problem is that although it knows to not translate k because it is a semiring, it still thinks that it should translate SMul R S
Yaël Dillies (Aug 19 2025 at 09:36):
In your example, R appears along with k in IsScalarTower R S k. Since k isn't additivisable, this means that R isn't either
Yaël Dillies (Aug 19 2025 at 09:36):
That's the only constraint I could find
Jovan Gerbscheid (Aug 19 2025 at 09:37):
Yes, but the issue is that at that point to_additive has already additivized SMul R S
Yaël Dillies (Aug 19 2025 at 09:39):
I seriously doubt to_additive can be implemented with a single-pass algorithm
Jovan Gerbscheid (Aug 19 2025 at 09:45):
My new idea, which is essentially what I had suggested at the start of this thread, is to have a way to tell to_additive which type arguments should not be translated. Then to_additive should treat these local variables the same way as it does constants marked with to_additive_dont_translate.
And I would like it to infer these arguments automatically when possible, so that most of the time you can just write @[to_additive]. And in edge cases like my example above you might need to write [to_additive (dont_translate := R S)] (maybe the logic could be beefed up to do this automatically as well)
Jovan Gerbscheid (Aug 19 2025 at 09:46):
By only looking at the type arguments, we can still fully support MulOpposite.
Jovan Gerbscheid (Aug 19 2025 at 09:48):
btw, from the above PR, I've split out a small (general) fix: #28625
Jovan Gerbscheid (Aug 19 2025 at 13:51):
I have now implemented (half of) my alternative fix at #28637. It implements the (dont_translate := ...) syntax, but doesn't implement any heuristics for figuring out the values automatically. Although I haven't yet tested this for MonoidAlgebra.
Jovan Gerbscheid (Aug 19 2025 at 13:58):
@Yaël Dillies, another option is that you do both at the same time: refactor MonoidAlgebra while also switching it to use to_additive. You don't need the (dont_translate := ...) heuristic to get started with this. It would just be nice to have. And this would be a good test case for #28637
Yaël Dillies (Aug 19 2025 at 13:59):
Oh god, that's probably a bad idea review-wise
Yaël Dillies (Aug 19 2025 at 14:02):
Why do it simultaneously? Is the reason that I'm redefining MonoidAlgebra R M as AddMonoidAlgebra R (Multiplicative M)?
Jovan Gerbscheid (Aug 19 2025 at 14:03):
No, I just though it could be more efficient
Jovan Gerbscheid (Aug 27 2025 at 11:53):
In #28637, I have now to_additive-ized MonoidAlgebra, to show that it works, inserting (dont_translate := k) whenever necessary. So, it is ready for review (and I'd be happy to split off the MonoidAlgebra part into a separate PR).
Some issues with additivizing showed up when the MonoidAlgebra and AddMonoidAlgebra theorems don't have the same shape. In particular MonoidAlgebra.of : G →* MonoidAlgebra k G is annoying, as its additive version is Multiplicative G →* AddMonoidAlgebra k G. We may want to consider making it into just a function G → MonoidAlgebra k G.
Additionally AddMonoidAlgebra.opRingEquiv isn't actually the additivization of MonoidAlgebra.opRingEquiv, which is a bit annoying. But it seems that both versions are genuinely being used.
Last updated: Dec 20 2025 at 21:32 UTC