Addition of zero in the coercion model

140 views
Skip to first unread message

Jonas Jermann

unread,
Feb 21, 2015, 3:39:35 PM2/21/15
to sage-...@googlegroups.com
Hi

When I do "0 + myElement" the coercion framework seems to map this
to a common parent of 0 and myElement (by checking the parent of 0 and
myElement).

ZZ does not coerce into myElement.parent() so I end up in a much
larger space than myElement.parent(). However I would prefer if adding
zero didn't change the parent space. One reason is for instance that
sum(...) no longer works properly because it always prepends "0+".

Eg: from sage.modular.modform_hecketriangle.all import *
myElement = ModularForms().E4()
myElement.parent() != sum([myElement]).parent()

Clearly "0" is a special case (the zero subspace does coerce into any of
the spaces).

How should I fix this?


Best
Jonas

Simon King

unread,
Feb 21, 2015, 3:47:55 PM2/21/15
to sage-...@googlegroups.com
Hi Jonas,

On 2015-02-21, Jonas Jermann <jjer...@gmail.com> wrote:
> ZZ does not coerce into myElement.parent() so I end up in a much
> larger space than myElement.parent(). However I would prefer if adding
> zero didn't change the parent space.

If ZZ does not coerce into the parent of your element, then the parent
is not a (unitary) ring.

> One reason is for instance that
> sum(...) no longer works properly because it always prepends "0+".

You can avoid it by explicitly providing the starting element to the sum
function.

> Eg: from sage.modular.modform_hecketriangle.all import *
> myElement = ModularForms().E4()
> myElement.parent() != sum([myElement]).parent()
>
> Clearly "0" is a special case (the zero subspace does coerce into any of
> the spaces).
>
> How should I fix this?

By providing a starting point for the summation:
sage: sum([myElement]).parent()
ModularFormsRing(n=3) over Integer Ring
sage: myElement.parent()
ModularForms(n=3, k=4, ep=1) over Integer Ring
sage: sum([myElement], myElement.parent().zero()).parent()
ModularForms(n=3, k=4, ep=1) over Integer Ring

Best regards,
Simon

Simon King

unread,
Feb 21, 2015, 3:57:44 PM2/21/15
to sage-...@googlegroups.com
On 2015-02-21, Simon King <simon...@uni-jena.de> wrote:
> If ZZ does not coerce into the parent of your element, then the parent
> is not a (unitary) ring.

PS:
And if it is not a ring, then many things don't work as smoothly as they
should.

For example, if P is a commutative additive group with an element x, then most
people would expect that x+0 should be equal to x. Reason: The ADDITIVE
group of ZZ coerces into P. However, Sage wants to coerce the RING ZZ
into P. It would be quite challanging to extend Sage's coercion model so
that the same parent (here: ZZ) can play different roles in coercion.

I.e., if P is a commutative additive group, then P.coerce_map_from(ZZ)
should return a morphism in the category of commutative additive groups.
Then, x+0 should work (because the coercion map is a morphism in the
category of additive groups), but x*0 should not work (because it is not
a morphism of multiplicative groups).

Best regards,
Simon


Jonas Jermann

unread,
Feb 21, 2015, 3:59:05 PM2/21/15
to sage-...@googlegroups.com
Hi Simon

On 21.02.2015 21:47, Simon King wrote:
> Hi Jonas,
>
> On 2015-02-21, Jonas Jermann <jjer...@gmail.com> wrote:
>> ZZ does not coerce into myElement.parent() so I end up in a much
>> larger space than myElement.parent(). However I would prefer if adding
>> zero didn't change the parent space.
>
> If ZZ does not coerce into the parent of your element, then the parent
> is not a (unitary) ring.

The parent is a vector space / module not a ring. Every vector space
contains zero, so in my opinion from a conceptual point of view
adding zero should not change the parent space.

However since it views 0 as an integer and not as (an element) the zero
subspace (unless we forcefully map it there) it seems hard to change
this behavior. :-(

>> One reason is for instance that
>> sum(...) no longer works properly because it always prepends "0+".
>
> You can avoid it by explicitly providing the starting element to the sum
> function.
>
>> Eg: from sage.modular.modform_hecketriangle.all import *
>> myElement = ModularForms().E4()
>> myElement.parent() != sum([myElement]).parent()
>>
>> Clearly "0" is a special case (the zero subspace does coerce into any of
>> the spaces).
>>
>> How should I fix this?
>
> By providing a starting point for the summation:
> sage: sum([myElement]).parent()
> ModularFormsRing(n=3) over Integer Ring
> sage: myElement.parent()
> ModularForms(n=3, k=4, ep=1) over Integer Ring
> sage: sum([myElement], myElement.parent().zero()).parent()
> ModularForms(n=3, k=4, ep=1) over Integer Ring

Thanks. Yes, I was aware of this, for instance I could also use
python's reduce function or a for loop. I was hoping to fix the issue
in a proper way, namely to get "adding zero" to work. Always having to
work around sum is annoying. What if "sum" is called outside of my
control in some other function? The issue can also be a hidden source
for bugs.


Best
Jonas

Jonas Jermann

unread,
Feb 21, 2015, 4:02:25 PM2/21/15
to sage-...@googlegroups.com
Hi

On 21.02.2015 21:57, Simon King wrote:
> On 2015-02-21, Simon King <simon...@uni-jena.de> wrote:
>> If ZZ does not coerce into the parent of your element, then the parent
>> is not a (unitary) ring.
>
> PS:
> And if it is not a ring, then many things don't work as smoothly as they
> should.
>
> For example, if P is a commutative additive group with an element x, then most
> people would expect that x+0 should be equal to x. Reason: The ADDITIVE
> group of ZZ coerces into P.

I'm confused. Why does the additive group of ZZ coerce into P?


Best
Jonas

Simon King

unread,
Feb 21, 2015, 4:12:25 PM2/21/15
to sage-...@googlegroups.com
Hi Jonas,

On 2015-02-21, Jonas Jermann <jjer...@gmail.com> wrote:
> I'm confused. Why does the additive group of ZZ coerce into P?

Oops, you are right. You can map ZZ onto an additive subgroup
of P, but that would be not canonical and hence not a coercion.

Anyway, Sage's coercion model works on the level of parents, not on the
level of elements. Hence, the fact that the element 0 has a canonical
interpretation in P is not relevant. It only matters whether or not ZZ
has a canonical morphism to P.

Best regards,
Simon

Nils Bruin

unread,
Feb 21, 2015, 4:13:00 PM2/21/15
to sage-...@googlegroups.com
On Saturday, February 21, 2015 at 12:57:44 PM UTC-8, Simon King wrote:

I.e., if P is a commutative additive group, then P.coerce_map_from(ZZ)
should return a morphism in the category of commutative additive groups.
Then, x+0 should work (because the coercion map is a morphism in the
category of additive groups), but x*0 should not work (because it is not
a morphism of multiplicative groups).

I'm not so sure. How does x+3 make unambiguous sense? We can map ZZ onto any cyclic subgroup. There is not necessarily a unique maximal one (for x+0 it obviously doesn't matter which one we choose, though)

On the other hand,  x*0 should work: A commutative additive group is a left and right ZZ-module, so x*0 and 0*x should match as scalar multiplication.

Jonas Jermann

unread,
Feb 21, 2015, 4:20:20 PM2/21/15
to sage-...@googlegroups.com
Hi

On 21.02.2015 22:13, Nils Bruin wrote:
> On Saturday, February 21, 2015 at 12:57:44 PM UTC-8, Simon King wrote:
>
>
> I.e., if P is a commutative additive group, then P.coerce_map_from(ZZ)
> should return a morphism in the category of commutative additive
> groups.
> Then, x+0 should work (because the coercion map is a morphism in the
> category of additive groups), but x*0 should not work (because it is
> not
> a morphism of multiplicative groups).
>
>
> I'm not so sure. How does x+3 make unambiguous sense? We can map ZZ onto
> any cyclic subgroup. There is not necessarily a unique maximal one (for
> x+0 it obviously doesn't matter which one we choose, though)

x+3 makes no sense. I was trying to argue that x+0 makes sense. But
I guess it works against the way the coercion model is set up. So I will
just have to deal with "+0" and "sum()" issues I guess.

Thanks to both of you and Simon for the clarifications.
So I will consider the issue closed/answered.


Best
Jonas

P.S. in sage.modular.modform_hecketriangle I implemented both, the ring
of modular forms and the vector spaces (homogeneous parts of the graded
ring). Note in particular that I implemented it in such a way that
multiplication of module elements give again module elements (in a
different space). Check it ouf if you're interested.

Simon King

unread,
Feb 21, 2015, 4:34:47 PM2/21/15
to sage-...@googlegroups.com
Hi Nils,

On 2015-02-21, Nils Bruin <nbr...@sfu.ca> wrote:
> I'm not so sure. How does x+3 make unambiguous sense?

If there is a coercion then (by definition of coercion in contrast to
conversion) it is unique.

> We can map ZZ onto
> any cyclic subgroup.

Yes, but at most one map is a coercion.

> On the other hand, x*0 should work: A commutative additive group is a left
> and right ZZ-module, so x*0 and 0*x should match as scalar multiplication.

Actually, in the case at hand it will probably work, because there is a
module action of ZZ on the parent. So, I should perhaps withdraw my
example, as n*x should work for any element n of ZZ and any element x of
a commutative additive group P, as P is a ZZ-module.

But what I don't need to withdraw is the general statement that coercion should
work on the level of parents, not on the level of elements. I.e., I
withdraw my example, because there is an action (involving the *whole*
parents). However, element-wise rules such as "if the first factor lives in an
additive group and the right factor is the neutral element for the additive group
of its parent, then the product shall be the zero element of the left factor"
should be banned.

Best regards,
Simon


Eric Gourgoulhon

unread,
Feb 21, 2015, 4:59:57 PM2/21/15
to sage-...@googlegroups.com
Hi,


Le samedi 21 février 2015 21:59:05 UTC+1, Jonas Jermann a écrit :

The parent is a vector space / module not a ring. Every vector space
contains zero, so in my opinion from a conceptual point of view
adding zero should not change the parent space.

However since it views 0 as an integer and not as (an element) the zero
subspace (unless we forcefully map it there) it seems hard to change
this behavior. :-(


It seems that a possible way to have 0 + MyElement work even if ZZ does not coerce to MyElement.parent() is to have MyElement.parent()._element_constructor_ accept 0 (i.e. Integer(0)) as an argument and return the zero element of your vector space / module for such an argument.  Am I correct?

Eric.

Jonas Jermann

unread,
Feb 21, 2015, 5:09:24 PM2/21/15
to sage-...@googlegroups.com
Hi

It's a good idea but it won't work (in fact the _element_constructor_
does accept zero correctly in my case). The coercion framework _first_
tries to find a common parent and _then_ the rest happens.

Best
Jonas
> --
> You received this message because you are subscribed to the Google
> Groups "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to sage-devel+...@googlegroups.com
> <mailto:sage-devel+...@googlegroups.com>.
> To post to this group, send email to sage-...@googlegroups.com
> <mailto:sage-...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/sage-devel.
> For more options, visit https://groups.google.com/d/optout.

Eric Gourgoulhon

unread,
Feb 21, 2015, 5:44:15 PM2/21/15
to sage-...@googlegroups.com


Le samedi 21 février 2015 23:09:24 UTC+1, Jonas Jermann a écrit :
Hi

It's a good idea but it won't work (in fact the _element_constructor_
does accept zero correctly in my case). The coercion framework _first_
tries to find a common parent and _then_ the rest happens.

At least in the cases I've tried (free modules as implemented in #15916), if no common parent is found, there is then a conversion of 0 via _element_constructor_, followed by the addition via the _add_ (single underscore) of the module elements.
Do I understand correctly that 0 + whatever first calls sage.structure.element.RingElement.__add__, which starts by
  if have_same_parent(left, right):
(line 1570 of sage/structure/element.pyx);  if this fails, it performs
  return coercion_model.bin_op(left, right, add)
(line 1579)
and the latter shall invoke your parent._element_constructor_ to convert 0 to your zero element. I am not completely sure about this... (this is my understanding why it works in my case).

Eric.

Simon King

unread,
Feb 21, 2015, 6:28:42 PM2/21/15
to sage-...@googlegroups.com
Hi Eric,

On 2015-02-21, Eric Gourgoulhon <egourg...@gmail.com> wrote:
> It seems that a possible way to have 0 + MyElement work even if ZZ does=
>=20
> not coerce to MyElement.parent() is to have=20
> MyElement.parent()._element_constructor_ accept 0 (i.e. Integer(0)) as an=
>=20
> argument and return the zero element of your vector space / module for such=
>=20
> an argument. Am I correct?=20

That's conversion. It would *not* enable x+0.

Best regards,
Simon

Jonas Jermann

unread,
Feb 21, 2015, 6:39:48 PM2/21/15
to sage-...@googlegroups.com
Hi

In my case "0 + MyElement" does find a common parent,
namely the modular forms _ring_.

So basically in my case "0 + some_element" is the same
as "some_element.as_ring_element()", unless the weight of
some_element (resp. its parent) is 0 with multiplier 1
in which case ZZ coerces into the space.

In any case, it's ok. I can work around these issues.


Best
Jonas

Eric Gourgoulhon

unread,
Feb 22, 2015, 5:00:10 AM2/22/15
to sage-...@googlegroups.com
Hi Simon,

Yes that's conversion, but both 0+x and x+0 works because, I think, of lines 979-990 of
sage.structure.coerce.pyx:
        # Allow coercion of 0 even if no coercion from Z
       
if is_Integer(x) and not x and type(yp) is not type:
           
try:
               
return yp(0), y
           
except Exception:
               
self._record_exception()

       
if is_Integer(y) and not y and type(xp) is not type:
           
try:
               
return x, xp(0)
           
except Exception:
               
self._record_exception()


Here is the example on free modules of #15916:

sage: M = FiniteRankFreeModule(ZZ, 3, name='M')
sage: e = M.basis('e')
sage: x = M([1,-2,3], basis=e) ; x
Element of the Rank-3 free module M over the Integer Ring
sage: x.display()
e_0 - 2 e_1 + 3 e_2

There is conversion of 0 to an element of M, but no coercion of ZZ into M:

sage: M(0)
Element zero of the Rank-3 free module M over the Integer Ring
sage: M.has_coerce_map_from(ZZ)
False

Because of the lines of coerce.pyx listed above, we have:

sage: cm = sage.structure.element.get_coercion_model()
sage: cm.canonical_coercion(0, x)
(Element zero of the Rank-3 free module M over the Integer Ring,
 Element of the Rank-3 free module M over the Integer Ring)
sage: cm.canonical_coercion(x, 0)
(Element of the Rank-3 free module M over the Integer Ring,
 Element zero of the Rank-3 free module M over the Integer Ring)

so that both x+0 and 0+x works:

sage: s = 0 + x ; s
Element of the Rank-3 free module M over the Integer Ring
sage: s.display()
e_0 - 2 e_1 + 3 e_2
sage: s = x + 0 ; s
Element of the Rank-3 free module M over the Integer Ring
sage: s.display()
e_0 - 2 e_1 + 3 e_2

Because there is "is_Integer(x)" in line 980 of coerce.pyx, this does not work for a 0 different from ZZ(0):

sage: zq = QQ(0)
sage: cm.canonical_coercion(zq, x)
...
TypeError: no common canonical parent for objects with parents: 'Rational Field' and 'Rank-3 free module M over the Integer Ring'

Indeed:
sage: s = zq + x
...
TypeError: unsupported operand parent(s) for '+': 'Rational Field' and 'Rank-3 free module M over the Integer Ring'


Best regards,

Eric.


John Cremona

unread,
Feb 22, 2015, 7:59:30 AM2/22/15
to SAGE devel
My 2p-worth: this all seems to be happening because of the implicit
automatic conversion from a literal 0 in Sage code to ZZ(0) in
contexts where a different zero exists (that of the parent of the
object to which 0 is going to be added).

Is there a case for having a separate global type or class for 0, to
which a literal 0 is coerced automatically on input, and which coerces
into every structure which has a 0 element? This seems to make sense
to me (if I simply input 0, or assign x=0, Sage cannot know yet
whether I mean the Integer 0 or something else). But I am not expert
in coercion and perhaps this would be hard (or impossible, or just
tedious) to implement.

John

>
>
> --
> You received this message because you are subscribed to the Google Groups
> "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sage-devel+...@googlegroups.com.
> To post to this group, send email to sage-...@googlegroups.com.

Simon King

unread,
Feb 22, 2015, 9:24:53 AM2/22/15
to sage-...@googlegroups.com
Hi Eric,

On 2015-02-22, Eric Gourgoulhon <egourg...@gmail.com> wrote:
> Yes that's conversion, but both 0+x and x+0 works because, I think, of=20
> lines 979-990 of=20
> sage.structure.coerce.pyx:
> # Allow coercion of 0 even if no coercion from Z
> if is_Integer(x) and not x and type(yp) is not type:
> try:
> return yp(0), y
> except Exception:
> self._record_exception()
>
> if is_Integer(y) and not y and type(xp) is not type:
> try:
> return x, xp(0)
> except Exception:
> self._record_exception()
> ...
> so that both x+0 and 0+x works:

Seriously? I didn't know that Sage's coercion model has such special
cases. OK, it makes it possible to get a typical usecase with least effort.
But my impression is that ultimately such special cases cause a lot more
confusion than a clear model in the spirit of "arithmetics across
parents relies on coercion morphisms, which are canonical morphisms in
suitable categories, and the composition of coercion morphisms is a
coercion morphism".

Note this:
sage: from sage.rings.integer import is_Integer
sage: is_Integer(int(0))
False

I guess that explains why "0+x+y" works on the command line but not in a
python module, and why "sum([x,y])" doesn't work either.

Best regards,
Simon


Eric Gourgoulhon

unread,
Feb 22, 2015, 10:18:34 AM2/22/15
to sage-...@googlegroups.com

Le dimanche 22 février 2015 15:24:53 UTC+1, Simon King a écrit :

Seriously? I didn't know that Sage's coercion model has such special
cases. OK, it makes it possible to get a typical usecase with least effort.
But my impression is that ultimately such special cases cause a lot more
confusion than a clear model in the spirit of "arithmetics across
parents relies on coercion morphisms, which are canonical morphisms in
suitable categories, and the composition of coercion morphisms is a
coercion morphism".


I agree. Actually, I've discovered the quoted lines of sage.structure.coerce.pyx while trying to understand why 0+x works while QQ(0) + x does not.

Note this:
  sage: from sage.rings.integer import is_Integer
  sage: is_Integer(int(0))
  False

Note that the function is_Integer used in sage.structure.coerce.pyx is *not* the above one: it is defined in lines 134-139 and it returns True for is_Integer(int(0)).

Best regards,

Eric.

Simon King

unread,
Feb 22, 2015, 10:40:04 AM2/22/15
to sage-...@googlegroups.com
Hi Eric,

On 2015-02-22, Eric Gourgoulhon <egourg...@gmail.com> wrote:
> Note that the function is_Integer used in sage.structure.coerce.pyx is=20
> *not* the above one: it is defined in lines 134-139 and it returns True for=
> is_Integer(int(0)).=20

Seriously? But when the integer zero is treated in a special way, then why
did "sum(...)" not work in the original post, while "sum(..., P.zero())"
did?

Best regards,
Simon

Jeroen Demeyer

unread,
Feb 22, 2015, 3:25:02 PM2/22/15
to sage-...@googlegroups.com
On 2015-02-22 15:24, Simon King wrote:
> Seriously? I didn't know that Sage's coercion model has such special
> cases. OK, it makes it possible to get a typical usecase with least effort.
> But my impression is that ultimately such special cases cause a lot more
> confusion than a clear model in the spirit of "arithmetics across
> parents relies on coercion morphisms, which are canonical morphisms in
> suitable categories, and the composition of coercion morphisms is a
> coercion morphism".

On the other, this special case is consistent with the idea of the
"univeral 0 object" proposed by John Cremona.

Eric Gourgoulhon

unread,
Feb 23, 2015, 3:40:55 AM2/23/15
to sage-...@googlegroups.com
Hi,


Le dimanche 22 février 2015 21:25:02 UTC+1, Jeroen Demeyer a écrit :

On the other, this special case is consistent with the idea of the
"univeral 0 object" proposed by John Cremona.

Indeed. In this respect, it is probably safe to use the literal 0 provided one makes sure that it is treated as Integer(0)  (Sage interpreter) *or* int(0) (Python code), which sage.structure.coerce.pyx does properly (via its function is_Integer).

Best wishes,

Eric.

Jonas Jermann

unread,
Feb 23, 2015, 2:16:10 PM2/23/15
to sage-...@googlegroups.com
Hi

In my case there is a cm.coercion_map(parent(myElement), parent(0))
(line 907) namely the coercions into the modular forms ring.
These maps are then used to map both myElement and 0 to the modular
forms ring (in contrast to the (vector) space myElement.parent()).

Unfortunately the check for the special case 0 comes after this,
not before. :-(


Regards
Jonas

Nicolas M. Thiery

unread,
Mar 4, 2015, 3:11:46 AM3/4/15
to sage-...@googlegroups.com
On Sat, Feb 21, 2015 at 08:47:40PM +0000, Simon King wrote:
> > How should I fix this?
>
> By providing a starting point for the summation:
> sage: sum([myElement]).parent()
> ModularFormsRing(n=3) over Integer Ring
> sage: myElement.parent()
> ModularForms(n=3, k=4, ep=1) over Integer Ring
> sage: sum([myElement], myElement.parent().zero()).parent()
> ModularForms(n=3, k=4, ep=1) over Integer Ring

Alternatively, you can use the form:

parent.sum(elements)

whose role is precisely to use the zero of the parent just when needed
(that is if the list is empty).

Cheers,
Nicolas
--
Nicolas M. Thiéry "Isil" <nth...@users.sf.net>
http://Nicolas.Thiery.name/

John Cremona

unread,
Mar 4, 2015, 3:45:56 AM3/4/15
to SAGE devel
On 4 March 2015 at 08:11, Nicolas M. Thiery <Nicolas...@u-psud.fr> wrote:
> On Sat, Feb 21, 2015 at 08:47:40PM +0000, Simon King wrote:
>> > How should I fix this?
>>
>> By providing a starting point for the summation:
>> sage: sum([myElement]).parent()
>> ModularFormsRing(n=3) over Integer Ring
>> sage: myElement.parent()
>> ModularForms(n=3, k=4, ep=1) over Integer Ring
>> sage: sum([myElement], myElement.parent().zero()).parent()
>> ModularForms(n=3, k=4, ep=1) over Integer Ring
>
> Alternatively, you can use the form:
>
> parent.sum(elements)
>
> whose role is precisely to use the zero of the parent just when needed
> (that is if the list is empty).

That is a very nice idiom! I did not know that, but I will use it a
lot from now on.

John

>
> Cheers,
> Nicolas
> --
> Nicolas M. Thiéry "Isil" <nth...@users.sf.net>
> http://Nicolas.Thiery.name/
>
Reply all
Reply to author
Forward
0 new messages