parent == parent -> parent is parent

2 views
Skip to first unread message

Martin Albrecht

unread,
Oct 12, 2006, 1:41:26 PM10/12/06
to sage-...@googlegroups.com
Hello everyone,

I've got a quick comment on this task

"Try changing a.parent() == b.parent() conditions to a._parent is b._parent in
various places and see what breaks."

of the

http://sage.math.washington.edu:9001/DevelopersRoom

. As it seems pickling would - partially - fail:

sage: k=GF(2**17)
sage: type(k)
<class 'sage.rings.finite_field.FiniteField_ext_pari'>
sage: e = k.random_element()
sage: f = k.random_element()
sage: e.parent() is f.parent()
True
sage: loads(dumps(e)).parent() is loads(dumps(f)).parent()
False
sage: loads(dumps(e)).parent() == loads(dumps(f)).parent()
True


We might need to fall back to == if "is" didn't work or we need to work out a
pickling strategy for this.

So far,
Martin

--
name: Martin Albrecht
_pgp: http://pgp.mit.edu:11371/pks/lookup?op=get&search=0x8EF0DC99
_www: http://www.informatik.uni-bremen.de/~malb
_icq: 177334829
_aim: martinralbrecht
_jab: martinr...@jabber.ccc.de

William Stein

unread,
Oct 12, 2006, 2:04:09 PM10/12/06
to sage-...@googlegroups.com
On Thu, 12 Oct 2006 10:41:26 -0700, Martin Albrecht
<ma...@informatik.uni-bremen.de> wrote:
> I've got a quick comment on this task
>
> "Try changing a.parent() == b.parent() conditions to a._parent is
> b._parent in
> various places and see what breaks."
>
> of the
>
> http://sage.math.washington.edu:9001/DevelopersRoom
>
> . As it seems pickling would - partially - fail:
>
> sage: k=GF(2**17)
> sage: type(k)
> <class 'sage.rings.finite_field.FiniteField_ext_pari'>
> sage: e = k.random_element()
> sage: f = k.random_element()
> sage: e.parent() is f.parent()
> True
> sage: loads(dumps(e)).parent() is loads(dumps(f)).parent()
> False
> sage: loads(dumps(e)).parent() == loads(dumps(f)).parent()
> True
>
> We might need to fall back to == if "is" didn't work or we need to work
> out a
> pickling strategy for this.

Forutnately, Python is smart about such things so this is not a show
stopper,
e.g., if you put e and f together in the same pickle (e.g., as a list),
then they
come back with the same parent.

sage: k=GF(2**17)
sage: type(k)
<class 'sage.rings.finite_field.FiniteField_ext_pari'>

sage: e = k.random_element(); f = k.random_element()


sage: e.parent() is f.parent()
True

sage: v = [e,f]
sage: w = loads(dumps(v))
sage: w[0].parent() is w[1].parent()
True

This is the sort of situation that is most common in pickling.
E.g., if you save a session, the whole thing is saved as a single
object.

Another option would be to whenever possible make parents
unique throughout SAGE. Using __reduce__ (for extensions)
or by overloading __new__ for (new style) Python classes,
one can make the parent unique even on unpickling. I know
most options can be implemented, since (unfortunately!) pretty
much every possibility is already implemented somewhere in
SAGE for some parent object (which is horribly confusing and
inconsistent).

Regarding uniqueness and immutability, I discussed this a lot
with people at SAGE Days. My *proposal* (no decision yet):

1. Only immutable objects should be unique.
2. Whenever possible parent structures should be immutable, whereas
elements,
e.g., matrices, are often mutable for convenience of algorithms.
3. In particular, we should completely eliminate the possiblity of
changing
properties of parent objects after creation. E.g., you can not
change
how QQ['x'] prints its variable after it is created, and you can't
change the default precision of a padic ring.
4. There will be a unique polynomial ring in one variable with each
variable name.
5. Pickling and reloading a unique parent structure will return the
same object,
i.e., loads(dumps(R)) is R will be true.

I want to emphasize that this is a proposal and that no decision has been
made.
I'm confident I could implement it.

-- William



David Harvey

unread,
Oct 12, 2006, 3:30:01 PM10/12/06
to sage-...@googlegroups.com

On Oct 12, 2006, at 1:41 PM, Martin Albrecht wrote:

> "Try changing a.parent() == b.parent() conditions to a._parent is
> b._parent in
> various places and see what breaks."
>
> of the
>
> http://sage.math.washington.edu:9001/DevelopersRoom
>
> . As it seems pickling would - partially - fail:

Yeah I had been wondering about this.

> sage: k=GF(2**17)
> sage: type(k)
> <class 'sage.rings.finite_field.FiniteField_ext_pari'>
> sage: e = k.random_element()
> sage: f = k.random_element()
> sage: e.parent() is f.parent()
> True
> sage: loads(dumps(e)).parent() is loads(dumps(f)).parent()
> False
> sage: loads(dumps(e)).parent() == loads(dumps(f)).parent()
> True
>
>
> We might need to fall back to == if "is" didn't work or we need to
> work out a
> pickling strategy for this.

Also, there's the problem that really the above code should be
returning False, even for ==, because there is no canonical map
between two finite fields which happen to be the same size (unless
they're actually prime).

As william says, if they are pickled together, then there's no problem.

David

David Harvey

unread,
Oct 12, 2006, 3:34:01 PM10/12/06
to sage-...@googlegroups.com

On Oct 12, 2006, at 2:04 PM, William Stein wrote:

> Regarding uniqueness and immutability, I discussed this a lot
> with people at SAGE Days. My *proposal* (no decision yet):

[...]

> 4. There will be a unique polynomial ring in one variable
> with each
> variable name.

So you mean, whenever you create a polynomial ring with a certain
variable name, we look that up in a cache? So for example:

sage: R = QQ[["x"]]
sage: S = QQ[["x"]]
sage: T = QQ[["y"]]
sage: R is S
True
sage: R is T
False

What about

sage: R = QQ[["x"]][["w"]]

So can we hash entire rings to do this lookup? Or do we just store
the table in the ring itself, i.e. in this example, Q[["x"]] would
keep a list of polynomial rings over itself?

David

William Stein

unread,
Oct 13, 2006, 3:15:05 AM10/13/06
to sage-...@googlegroups.com
On Thu, 12 Oct 2006 12:34:01 -0700, David Harvey
<dmha...@math.harvard.edu> wrote:
> On Oct 12, 2006, at 2:04 PM, William Stein wrote:
>> Regarding uniqueness and immutability, I discussed this a lot
>> with people at SAGE Days. My *proposal* (no decision yet):
>
> [...]
>
>> 4. There will be a unique polynomial ring in one variable
>> with each
>> variable name.
>
> So you mean, whenever you create a polynomial ring with a certain
> variable name, we look that up in a cache? So for example:

yes. Sorry for not including code snippets to make what I was suggesting
clear (you are very good at making such code snippets).

> sage: R = QQ[["x"]]
> sage: S = QQ[["x"]]
> sage: T = QQ[["y"]]
> sage: R is S
> True
> sage: R is T
> False
>
> What about
>
> sage: R = QQ[["x"]][["w"]]
>
> So can we hash entire rings to do this lookup? Or do we just store
> the table in the ring itself, i.e. in this example, Q[["x"]] would
> keep a list of polynomial rings over itself?

I had in mind using the weakref module to cache all rings create
in SAGE. The cache would be a module-scope variable in
sage/rings/polynomial_ring.py.
In fact it is already implemented -- but commented out since we needed to
clarify the semantics.

In short:
* Immutable parent objects should be globally cached with weakrefs
* We make most parent objects immutable -- which matches *much* more
what one has in
mathematics. E.g., If one says in a paper, "Let R = Z[x]." then if x
were suddenly
written as "y" elsewhere in the paper readers would be unhappy. It
would be fine
to define a ring "S = Z[y]" and fix the ring map sending x to y, but
it would not
be fine to simply start writing "x" as "y".

From this point of view, there is no real difference between 'R =
QQ[["x"]][["w"]]'
and 'R = QQ["x"]'. The key for the dictioniary for 'QQ[["x"]][["w"]]' is
the hash
of QQ["x"], and the key for QQ["x"] is QQ. Since rings are parent
structures, hence
immutable (as mathematical objects), they are hasheable.

William

William Stein

unread,
Oct 13, 2006, 3:15:07 AM10/13/06
to sage-...@googlegroups.com

For the record -- I agree with this.

William

Martin Albrecht

unread,
Oct 13, 2006, 4:45:39 AM10/13/06
to sage-...@googlegroups.com
On Friday 13 October 2006 07:15, William Stein wrote:
> On Thu, 12 Oct 2006 12:30:01 -0700, David Harvey
> > Also, there's the problem that really the above code should be
> > returning False, even for ==, because there is no canonical map
> > between two finite fields which happen to be the same size (unless
> > they're actually prime).
>
> For the record -- I agree with this.

I'm confused, this is what FiniteField_ext_pari performs if two finite
extension fields are compared:

if (self is other) or (self.__order == other.__order and
self.variable_name() == other.variable_name() \
and self.__modulus == other.__modulus):
return 0

If order, variable name, and modulus agree, the canonical map would be the
identity map. Why should this return False? Why are you assuming only the
sizes are compared? What am I missing?

_jab: martinr...@jabber.ccc.de

Martin Albrecht

unread,
Oct 13, 2006, 4:58:07 AM10/13/06
to sage-...@googlegroups.com
On Friday 13 October 2006 08:45, Martin Albrecht wrote:
> On Friday 13 October 2006 07:15, William Stein wrote:
> > On Thu, 12 Oct 2006 12:30:01 -0700, David Harvey
> >
> > > Also, there's the problem that really the above code should be
> > > returning False, even for ==, because there is no canonical map
> > > between two finite fields which happen to be the same size (unless
> > > they're actually prime).
> >
> > For the record -- I agree with this.
>
> I'm confused, this is what FiniteField_ext_pari performs if two finite
> extension fields are compared:
>
> if (self is other) or (self.__order == other.__order and
> self.variable_name() ==
> other.variable_name() \ and self.__modulus == other.__modulus): return 0
>
> If order, variable name, and modulus agree, the canonical map would be the
> identity map. Why should this return False? Why are you assuming only the
> sizes are compared? What am I missing?

After actually waking up I realized what you meant: As there is a two way map
there is no way of knowing which way to coerce, right? So comparison would
need to be non-commutative?

David Harvey

unread,
Oct 13, 2006, 8:17:25 AM10/13/06
to sage-...@googlegroups.com

No, I mean something quite different.

It depends whether you think of a finite field as carrying around the
extra information of a "modulus". Even though that's how they are
represented internally, it's an arbitrary choice, and I tend not to
think of it as carrying that information.

Here's an example, some of which is completely illegal SAGE code:

sage: R = GaussianIntegers()
sage: F1 = R / (7*R)
sage: F1.is_field()
True
sage: len(F1)
49

sage: S.<x> = PolynomialRing(Integers(7))
sage: f = x^2 - x + 3
sage: F2 = S / f
sage: F2.is_field()
True
sage: len(F2)
49

sage: F3 = FiniteField(7^2)
sage: F3.is_field()
True
sage: len(F3)
49

All of F1, F2, and F3 are isomorphic. They are all fields of order
49. But they are not *canonically* isomorphic. Given a generator in
one of them, there is no natural way to choose a matching generator
in another one. In other words:

sage: F3(x) # should raise an error, SAGE shouldn't know what to do

So I guess the difference between you and me, is that I want there to
be a "FiniteFieldWithoutChoiceOfGenerator" object, and you want there
to be a "FiniteFieldWithASpecifiedGenerator" object. The latter is
much easier to implement in a machine, but the former is in many
cases more accurate mathematically.

David

Martin Albrecht

unread,
Oct 13, 2006, 9:45:13 AM10/13/06
to sage-...@googlegroups.com
> No, I mean something quite different.

snip.

> So I guess the difference between you and me, is that I want there to
> be a "FiniteFieldWithoutChoiceOfGenerator" object, and you want there
> to be a "FiniteFieldWithASpecifiedGenerator" object. The latter is
> much easier to implement in a machine, but the former is in many
> cases more accurate mathematically.

Just out of curiosity: I've spend much time to ensure that - when using
Givaro - e.g. GF(2**8) always returns the same finite field, with the same
modulus and generator. Givaro - the C++ library - randomizes both modulus and
generator. Would that behavior be acceptable/desirable for you? I understand
that one often doesn't care which finite extension field instance of GF(2**8)
is used in a paper etc. but for a CAS i find it pretty natural to actually
fix a (default) generator?

David Harvey

unread,
Oct 13, 2006, 10:08:51 AM10/13/06
to sage-...@googlegroups.com

On Oct 13, 2006, at 9:45 AM, Martin Albrecht wrote:

>> So I guess the difference between you and me, is that I want there to
>> be a "FiniteFieldWithoutChoiceOfGenerator" object, and you want there
>> to be a "FiniteFieldWithASpecifiedGenerator" object. The latter is
>> much easier to implement in a machine, but the former is in many
>> cases more accurate mathematically.
>
> Just out of curiosity: I've spend much time to ensure that - when using
> Givaro - e.g. GF(2**8) always returns the same finite field, with the
> same
> modulus and generator. Givaro - the C++ library - randomizes both
> modulus and
> generator. Would that behavior be acceptable/desirable for you? I
> understand
> that one often doesn't care which finite extension field instance of
> GF(2**8)
> is used in a paper etc. but for a CAS i find it pretty natural to
> actually
> fix a (default) generator?

I'm not sure.

Suppose I want to work with a specific presentation of a finite field,
say

sage: R.<x> = PolynomialRing(Integers(2))
sage: f = x^8 + x^7 + x^3 + x + 1
sage: f.is_irreducible()
True
sage: F = R / f

Should SAGE be smart enough to implement this using a library like
Givaro? If so, does that mean it has to choose a map between my
representation and the underlying representation, and translate between
them on the fly?

David

William Stein

unread,
Oct 13, 2006, 12:20:44 PM10/13/06
to sage-...@googlegroups.com
On Fri, 13 Oct 2006 01:45:39 -0700, Martin Albrecht
<ma...@informatik.uni-bremen.de> wrote:
> On Friday 13 October 2006 07:15, William Stein wrote:
>> On Thu, 12 Oct 2006 12:30:01 -0700, David Harvey
>> > Also, there's the problem that really the above code should be
>> > returning False, even for ==, because there is no canonical map
>> > between two finite fields which happen to be the same size (unless
>> > they're actually prime).
>>
>> For the record -- I agree with this.
>
> I'm confused, this is what FiniteField_ext_pari performs if two finite
> extension fields are compared:
>
> if (self is other) or (self.__order == other.__order and
> self.variable_name() ==
> other.variable_name() \
> and self.__modulus == other.__modulus):
> return 0
>
> If order, variable name, and modulus agree, the canonical map would be
> the
> identity map. Why should this return False? Why are you assuming only the
> sizes are compared? What am I missing?

There is no *canonical* map F_q --> F_q in the category of finite fields
unless
q = p. A canonical map is one that is unique (up to unique isomorphism).
Understanding this sort of thing very well is one of those "rights of
passage"
that I remember well a bunch of us going through during graduate school.

EXAMPLES and COUNTEREXAMPLES:
* Z/pZ and F_p are canonically isomorphic
* Q[x] and Q[y] are *not* canonically isomorphic, since x|-->y is also a
ring homomorphism.
* A subscape of Q^n of full rank is canonically isomorphic to Q^n in the
category of vector spaces equipped with an embedding in a fixed ambient
space.
* Two groups of prime order p are not canonically isomorphic unless p=2.

Thinking about this, one *major* worry I have if we define == to be
canonical
isomorphism is that any object with automorphisms is not even == to itself!
E.g., F_q is not equal to itself, since it isn't canonically isomorphic
to itself. This argues for not defining == to be "canonically
isomorphic".

An alternative is that we could define == to be "there exists a 'natural'
SAGE
map in both direction", i.e., _coerce_ is defined to work in both
direction.
I.e., two objects are considered "==" if canonical coercion works in both
direction.
The result would be that if X == Y and x in X and y in Y, then x+y is
defined
only if X is Y.

William

William Stein

unread,
Oct 13, 2006, 12:59:30 PM10/13/06
to sage-...@googlegroups.com
On Fri, 13 Oct 2006 06:45:13 -0700, Martin Albrecht
<ma...@informatik.uni-bremen.de> wrote:

>
>> No, I mean something quite different.
>
> snip.
>
>> So I guess the difference between you and me, is that I want there to
>> be a "FiniteFieldWithoutChoiceOfGenerator" object, and you want there
>> to be a "FiniteFieldWithASpecifiedGenerator" object. The latter is
>> much easier to implement in a machine, but the former is in many
>> cases more accurate mathematically.

SAGE should only have finite field with choice of generator, I think.
Read the paper by Canon et al. about MAGMA to be convinced of the value
of this approach. It makes defining morphisms, etc., much more systematic.

william

David Harvey

unread,
Oct 13, 2006, 2:33:42 PM10/13/06
to sage-...@googlegroups.com

On Oct 13, 2006, at 12:59 PM, William Stein wrote:

>>> So I guess the difference between you and me, is that I want there to
>>> be a "FiniteFieldWithoutChoiceOfGenerator" object, and you want there
>>> to be a "FiniteFieldWithASpecifiedGenerator" object. The latter is
>>> much easier to implement in a machine, but the former is in many
>>> cases more accurate mathematically.
>
> SAGE should only have finite field with choice of generator, I think.
> Read the paper by Canon et al. about MAGMA to be convinced of the value
> of this approach. It makes defining morphisms, etc., much more
> systematic.

OK, but should the user be able to select the generator that is used
*for the underlying representation*? (Either explicitly, or implicitly
through whatever construction they were carrying out.) Or are we stuck
with whatever generator/modulus the underlying finite field library
chooses?

David

William Stein

unread,
Oct 13, 2006, 2:37:44 PM10/13/06
to sage-...@googlegroups.com

The user absolutely *must* have the option to choose it, though possibly
with
a performance penalty.

William

David Harvey

unread,
Oct 13, 2006, 2:46:46 PM10/13/06
to sage-...@googlegroups.com

On Oct 13, 2006, at 12:20 PM, William Stein wrote:

> Thinking about this, one *major* worry I have if we define == to be
> canonical isomorphism is that any object with automorphisms is not
> even == to itself!

I don't think this is true. There's always the identity map, which is
canonical.

Another way to look at it:

We could treat the category under consideration as:
* objects are finite fields, together with a chosen generator
* morphisms are ALL homomorphisms of finite fields (not just those
mapping generators to generators).

As far as I can see, this satisfies all the axioms of a category :-)

THEN, we distinguish certain morphisms as being "canonical" -- those
that send generators to generators.

We could do something similar with univariate polynomial rings. The
objects are polynomial rings, together with a chosen generator. The
morphisms are *all* morphisms of polynomial rings. We distinguish
canonical ones which send generators to generators.

I haven't thought about this too carefully, so maybe someone will
indulge me by shooting it down.

> An alternative is that we could define == to be "there exists a
> 'natural'
> SAGE
> map in both direction", i.e., _coerce_ is defined to work in both
> direction.
> I.e., two objects are considered "==" if canonical coercion works in
> both
> direction.
> The result would be that if X == Y and x in X and y in Y, then x+y is
> defined
> only if X is Y.

I guess what I said above is kind of the same as this.

David

Martin Albrecht

unread,
Oct 13, 2006, 3:11:41 PM10/13/06
to sage-...@googlegroups.com

Just some technical detail: For Givaro/Pari/NTL you may specify the generator.
No translation necessary. I think GAP does not have such an ability but I'm
not sure.

Martin

> David

Martin Albrecht

unread,
Oct 13, 2006, 3:44:23 PM10/13/06
to sage-...@googlegroups.com

But this assumes that the create-only-one-instance-of-a-category
infrastructure is in place, right?

sage: k1, x = GF(2**8).objgen()
sage: k2, y = GF(2**8).objgen()
sage: k1 == k2
True
sage: k1 is k2 # this would be True under the assumption
False
sage: x+y # this would work under the assumption
ArithmeticError, "x.parent() is not y.parent()"

_jab: martinr...@jabber.ccc.de

William Stein

unread,
Oct 14, 2006, 12:20:38 PM10/14/06
to sage-...@googlegroups.com
On Fri, 13 Oct 2006 12:44:23 -0700, Martin Albrecht
<ma...@informatik.uni-bremen.de> wrote:

>> I haven't thought about this too carefully, so maybe someone will
>> indulge me by shooting it down.
>>
>> > An alternative is that we could define == to be "there exists a
>> > 'natural'
>> > SAGE
>> > map in both direction", i.e., _coerce_ is defined to work in both
>> > direction.
>> > I.e., two objects are considered "==" if canonical coercion works in
>> > both
>> > direction.
>> > The result would be that if X == Y and x in X and y in Y, then x+y is
>> > defined
>> > only if X is Y.
>
> But this assumes that the create-only-one-instance-of-a-category
> infrastructure is in place, right?

It is. David Kohel and I implemented this nearly a year ago when he
visited
me in San Diego. For example,

sage: Rings()
Category of rings
sage: Rings() is Rings()
True

And again, I think the idea to make the map that sends generators to
generators
(in order!) canonical is a great idea, at least in some cases.
It would be nice to use Conway polynomials to define canonical consistent
embeddings between finite fields of different cardinalities (when they
exist), but such maps don't send generators to generators. Another
problematic
example is that QQ canonically maps into CC (in the category of fields),
but the
generator of QQ is 1 and the generator of CC is sqrt(-1). So we shouldn't
require that all canonical maps sends generators to generators, though a
morphism
that sends gens to gens in order should be considered canonical.

William

David Harvey

unread,
Oct 15, 2006, 6:41:58 PM10/15/06
to sage-...@googlegroups.com

On Oct 14, 2006, at 12:20 PM, William Stein wrote:

> but the
> generator of QQ is 1 and the generator of CC is sqrt(-1).

Why is that, anyway?

I can sort of see why the generator of CC could be sqrt(-1), at least
if you think of CC as an algebra over RR.

But what does it mean for the generator of QQ to be 1?

> So we shouldn't require that all canonical maps sends generators to
> generators, though a
> morphism that sends gens to gens in order should be considered
> canonical.

It would be really nice to have a straight answer on this question.

i.e. the question is:

"When are two things the same?"

Very deep. Or perhaps:

"It depends on what the meaning of the word 'is' is"
-- a famous non-mathematician

Without a clear vision on this question, I think a lot of things are
going to get bogged down. Like, I want to optimise the RingElement
arithmetic operations, but without knowing whether "==" or "is" is
the right thing to be doing, I can't do it.

Is the answer just going to be: "== means canonically isomorphic, and
a canonical map is anything that _coerce_ says is a canonical map"? I
can live with that I suppose, but I don't really like it.

David

David Joyner

unread,
Oct 15, 2006, 9:18:15 PM10/15/06
to sage-...@googlegroups.com
On 10/15/06, David Harvey <dmha...@math.harvard.edu> wrote:
I'm embarrassed to ask this on the list (but i will anyway)
since I think you've already answered this question on list and
even in an email to me. I can't remember or find your answer.
Of course, I frequently forget things I think I once knew so maybe
I'm embarrassed for no reason.

Question: Do you think "1 == 1.0" is true or not?
If you believe yes, then it seems to me to be consistent you must also
believe that coercion into other classes is okay. If no, then
it seems you must create a function which programmers
and users are very comfortable with since I think many
will want (for convenience) "1 == 1.0" to be true.

William Stein

unread,
Oct 15, 2006, 9:24:53 PM10/15/06
to sage-...@googlegroups.com
On Sun, 15 Oct 2006 18:18:15 -0700, David Joyner <wdjo...@gmail.com>
wrote:

> On 10/15/06, David Harvey <dmha...@math.harvard.edu> wrote:
> I'm embarrassed to ask this on the list (but i will anyway)
> since I think you've already answered this question on list and
> even in an email to me. I can't remember or find your answer.
> Of course, I frequently forget things I think I once knew so maybe
> I'm embarrassed for no reason.
>
> Question: Do you think "1 == 1.0" is true or not?
> If you believe yes, then it seems to me to be consistent you must also
> believe that coercion into other classes is okay. If no, then
> it seems you must create a function which programmers
> and users are very comfortable with since I think many
> will want (for convenience) "1 == 1.0" to be true.

I definitely think "1 == 1.0" should return True. This is because *in
SAGE* there
is a canonical coercion map

ZZ ---> RR

but *no* map in the other direction. Thus 1 is coerced to RR and compared
with 1.0 in RR (via mpfr's cmp function). This does return true.

William

David Harvey

unread,
Oct 15, 2006, 9:26:04 PM10/15/06
to sage-...@googlegroups.com

On Oct 15, 2006, at 9:18 PM, David Joyner wrote:

> Question: Do you think "1 == 1.0" is true or not?
> If you believe yes, then it seems to me to be consistent you must also
> believe that coercion into other classes is okay. If no, then
> it seems you must create a function which programmers
> and users are very comfortable with since I think many
> will want (for convenience) "1 == 1.0" to be true.

Two answers to this.

(1) I think it's a different question to the one I've been exploring.
I'm interested in "==" on the level of structures, not on the level
of elements. I haven't thought about it for elements very much. As
long as we very clearly separate structures and elements, they remain
two separate questions. (There could be some confusion if you start
talking about e.g. the category of abelian groups, whose "elements"
could be considered to be "structures", but I won't go there.)

(2) Now for your actual question: I'm not sure whether "1 == 1.0"
should be true. (For the record, I don't remember you asking me this
before :-)) This example is complicated because of issues about real
precision. Perhaps an easier question is "1 == x" where x is an
element of say Z/3Z. In either case though, I suppose the same
mechanism as e.g. the addition operator should come into play. First,
test if there's a canonical coercion in exactly one direction. If so,
perform the coercion, and do the equality test in the target domain.
I don't know if I really believe this though. It's not something I
think about in my own programming very much. I tend to be very "type-
aware", and I don't like performing operations on different types,
unless I know exactly what will be going on underneath.

David

David Joyner

unread,
Oct 15, 2006, 9:41:17 PM10/15/06
to sage-...@googlegroups.com
On 10/15/06, David Harvey <dmha...@math.harvard.edu> wrote:
>
>
>
> (2) Now for your actual question: I'm not sure whether "1 == 1.0"
> should be true. (For the record, I don't remember you asking me this
> before :-)) This example is complicated because of issues about real
> precision. Perhaps an easier question is "1 == x" where x is an
> element of say Z/3Z. In either case though, I suppose the same
> mechanism as e.g. the addition operator should come into play. First,
> test if there's a canonical coercion in exactly one direction. If so,
> perform the coercion, and do the equality test in the target domain.

If A, B are two structures (say A=ZZ, B=RR) and a in A,
b in B are elements for which "a==b" returns true then it seems
reasonable for a user to expect "a in B" and "b in A"
return true. But if A=ZZ and B=ZZ/3ZZ then it seems
your above statement implies "1==B(1) should be true.

> I don't know if I really believe this though. It's not something I

I am suspicious that the above mentioned asymmetry can be
resolved satisfactorily. Am I missing something?

David Harvey

unread,
Oct 15, 2006, 9:46:49 PM10/15/06
to sage-...@googlegroups.com

On Oct 15, 2006, at 9:41 PM, David Joyner wrote:

> If A, B are two structures (say A=ZZ, B=RR) and a in A,
> b in B are elements for which "a==b" returns true then it seems
> reasonable for a user to expect "a in B" and "b in A"
> return true.

Are you saying that "1.0 in ZZ" should be True?

I don't think I like this.

David

Justin C. Walker

unread,
Oct 15, 2006, 10:53:17 PM10/15/06
to sage-...@googlegroups.com

On Oct 15, 2006, at 18:24 , William Stein wrote:

>
> On Sun, 15 Oct 2006 18:18:15 -0700, David Joyner <wdjo...@gmail.com>
> wrote:
>> On 10/15/06, David Harvey <dmha...@math.harvard.edu> wrote:
>> I'm embarrassed to ask this on the list (but i will anyway)
>> since I think you've already answered this question on list and
>> even in an email to me. I can't remember or find your answer.
>> Of course, I frequently forget things I think I once knew so maybe
>> I'm embarrassed for no reason.
>>
>> Question: Do you think "1 == 1.0" is true or not?
>> If you believe yes, then it seems to me to be consistent you must
>> also
>> believe that coercion into other classes is okay. If no, then
>> it seems you must create a function which programmers
>> and users are very comfortable with since I think many
>> will want (for convenience) "1 == 1.0" to be true.
>
> I definitely think "1 == 1.0" should return True.

Assuming this is to be the case, how do we handle

sage: sin(1)
sin(1)

sage: sin(1.)
0.84147098480789650

Is this straight-forward?

If this isn't bad enough:

sage: maxima_console()
Maxima 5.9.3 http://maxima.sourceforge.net
Using Lisp CLISP 2.38 (2006-01-24)
Distributed under the GNU Public License. See the file COPYING.
Dedicated to the memory of William Schelter.
This is a development version of Maxima. The function bug_report()
provides bug reporting information.
(%i1) sin(1.0);
(%o1) .8414709848078965
(%i2) sin(1);
(%o2) sin(1)


> This is because *in SAGE* there is a canonical coercion map
>
> ZZ ---> RR
>
> but *no* map in the other direction. Thus 1 is coerced to RR and
> compared
> with 1.0 in RR (via mpfr's cmp function). This does return true.

I don't understand the issue of direction in this example. What
happens if there are maps in both directions?

Justin

--
Justin C. Walker, Curmudgeon-at-Large
() The ASCII Ribbon Campaign
/\ Help Cure HTML Email

William Stein

unread,
Oct 15, 2006, 10:54:55 PM10/15/06
to sage-...@googlegroups.com

The 1 == 1.0 should be true. Currently what SAGE does is precisely
what you outline above -- it uses the unique canonical coercion ZZ --> RR
to coerce 1 to RR, then uses mpfr's compare function. This happens to
output "True". The question of equality gets delegated in an easy to
understand and predict way in precisely the same way it does for
arithmetic.

William

Justin C. Walker

unread,
Oct 15, 2006, 11:08:07 PM10/15/06
to sage-...@googlegroups.com

On Oct 15, 2006, at 18:26 , David Harvey wrote:
> On Oct 15, 2006, at 9:18 PM, David Joyner wrote:
>> Question: Do you think "1 == 1.0" is true or not?
>> If you believe yes, then it seems to me to be consistent you must
>> also
>> believe that coercion into other classes is okay. If no, then
>> it seems you must create a function which programmers
>> and users are very comfortable with since I think many
>> will want (for convenience) "1 == 1.0" to be true.
>
> Two answers to this.
>
> (1) I think it's a different question to the one I've been exploring.
> I'm interested in "==" on the level of structures, not on the level
> of elements. I haven't thought about it for elements very much. As
> long as we very clearly separate structures and elements, they remain
> two separate questions. (There could be some confusion if you start
> talking about e.g. the category of abelian groups, whose "elements"
> could be considered to be "structures", but I won't go there.)

I agree on both counts: it's a different question, and you shouldn't
go there :-}

> (2) Now for your actual question: I'm not sure whether "1 == 1.0"
> should be true. (For the record, I don't remember you asking me this
> before :-)) This example is complicated because of issues about real
> precision. Perhaps an easier question is "1 == x" where x is an
> element of say Z/3Z.

Ick... this (which is the case in 1.3.7.2)

sage: Z3=IntegerModRing(3)

sage: One=Z3(1)

sage: One
1

sage: 1 == One
True

sage: 4 == One
True

is reasonable? The Principle of Least Surprise is certainly on the
edge here...

My problem here is the "hidden coercion": it's not clear to me what
does/will/should happen, in these cases.

> In either case though, I suppose the same
> mechanism as e.g. the addition operator should come into play. First,
> test if there's a canonical coercion in exactly one direction. If so,
> perform the coercion, and do the equality test in the target domain.

It's probably a measure of perversity (unrelated to sheaves) that
makes me feel that adding '1' and 'One', with automatic coercion to Z/
3Z, makes sense; while comparing '4' and 'One' does not.

> I don't know if I really believe this though. It's not something I
> think about in my own programming very much. I tend to be very "type-
> aware", and I don't like performing operations on different types,
> unless I know exactly what will be going on underneath.

I agree in principle, but there are a couple of things to consider:
- 'exactly' is a slippery concept; it differs for the mathematician
using SAGE and the programmer+mathematician using and developing
SAGE;
- as SAGE gets more capability, and perhaps relies on more subsystems
(Pari, maxima, ...), this sort of thing becomes more difficult to
manage. At some point, very few of us will carry this kind of thing
around in our head (collective or otherwise).

David Harvey

unread,
Oct 15, 2006, 11:18:59 PM10/15/06
to sage-...@googlegroups.com

On Oct 15, 2006, at 11:08 PM, Justin C. Walker wrote:

> sage: Z3=IntegerModRing(3)
>
> sage: One=Z3(1)
>
> sage: One
> 1
>
> sage: 1 == One
> True
>
> sage: 4 == One
> True
>
> is reasonable? The Principle of Least Surprise is certainly on the
> edge here...
>
> My problem here is the "hidden coercion": it's not clear to me what
> does/will/should happen, in these cases.

Yeah, it's a good example.

I guess the difficulty is that it feels like "a == b" should include
the statement "a is the same type as b". Which is not true with our
definition of == above.

(Perhaps part of the problem is that One prints as "1". Maybe it
would be good if it printed as something like "1 mod 3". Or maybe
that would be bad, because your screen would always be getting
cluttered.)

>> In either case though, I suppose the same
>> mechanism as e.g. the addition operator should come into play. First,
>> test if there's a canonical coercion in exactly one direction. If so,
>> perform the coercion, and do the equality test in the target domain.
>
> It's probably a measure of perversity (unrelated to sheaves) that
> makes me feel that adding '1' and 'One', with automatic coercion to Z/
> 3Z, makes sense; while comparing '4' and 'One' does not.

It's not that perverse. The thing is, adding 1 and One is supposed to
give you an answer in one of the parents, whereas comparing 4 and One
is supposed to give you a boolean, which is totally unrelated, so you
don't think too hard about where the answer is going to end up and
how it could have ended up there.

Still, I think I prefer the coercion version of things. There is
something to be said for having consistency here. Maybe it's just one
of things that one is just supposed to get used to, like it took me a
while to get used to Python's name binding semantics.

David

William Stein

unread,
Oct 16, 2006, 2:17:41 AM10/16/06
to sage-...@googlegroups.com
Hello,

The following is what has emerged from the discussion about equality,
parents, etc.

Let R and S be SAGE parent structures, e.g., groups, rings, point sets,
etc.

* HOW _coerce_ WORKS:
Suppose a _coerce_ map R --> S is defined. Then:

(1) R.category() must be a subcategory of S.category().
(2) The set-theoretic map R --> S defined by _coerce_ must
define a morphism in S.category(); in particular, it must be
defined on all of R. It does not have to be injective.
(3) If coerce is defined in both direction, i.e., R --> S and S
--> R,
then the composition in both directions must be the identity
maps.
(4) _coerce_ should send generators to generators when this makes
sense.
(5) If R is S, then _coerce_ must be the identity map.

In implementing _coerce_, we are making a fixed choice of
identifications
and inclusions throughout SAGE that follow the above rules.
Embeddings of
finite fields via Conway polynomials would fit into this
structure. As would
the maps QQ --> CC and ZZ --> ZZ/n*ZZ. _coerce_ does not have to
be canonical
in a precisely defined mathematical sense.

* HOW __call__ WORKS:
__call__ makes an object of R from an element x of anything, if at
all possible.
__call__ is never called implicitly by binary operators.

* ARITHMETIC __add__, __mul__, etc.:
When doing a binary operation, if the parents are not identical
(in the sense of is), determine if precisely one _coerce_ map is
defined;
if so, apply it and do the arithmetic operation. If not, raise
a TypeError. (Whether or not there is a coerce map between objects
should be cached for efficiency.)

* PARENT OBJECT __cmp__:
"R == S" by definition means that _coerce_ is defined in both
directions.

* ELEMENT __cmp__:
If the parents aren't identical, test if
precisely one _coerce_ map is defined -- if so, return __cmp__
after applying the coerce. If both coercisions are defined, compute
both __cmp__'s (in both R and S). Return the value if they give
the same results. Otherwise return -1 (the convention in Python
is that __cmp__ never raises an exception). If no _coerce_ is
defined return -1 (i.e., not equal).

* IN __contains__:
"x in R" should be true if and only if R._coerce_(x) would not
raise a TypeError.

Unless anybody has serious object, I will put the above in an official
place, so we can get on with things.

-- William

Robert Bradshaw

unread,
Oct 16, 2006, 2:37:29 PM10/16/06
to sage-...@googlegroups.com

On Oct 15, 2006, at 8:08 PM, Justin C. Walker wrote:

>
>
> On Oct 15, 2006, at 18:26 , David Harvey wrote:
>> On Oct 15, 2006, at 9:18 PM, David Joyner wrote:

[...]

>> (2) Now for your actual question: I'm not sure whether "1 == 1.0"
>> should be true. (For the record, I don't remember you asking me this
>> before :-)) This example is complicated because of issues about real
>> precision. Perhaps an easier question is "1 == x" where x is an
>> element of say Z/3Z.
>
> Ick... this (which is the case in 1.3.7.2)
>
> sage: Z3=IntegerModRing(3)
>
> sage: One=Z3(1)
>
> sage: One
> 1
>
> sage: 1 == One
> True
>
> sage: 4 == One
> True
>
> is reasonable? The Principle of Least Surprise is certainly on the
> edge here...
>
> My problem here is the "hidden coercion": it's not clear to me what
> does/will/should happen, in these cases.

Just to add my two cents, this seems natural to me, and the coercion
definition of equality seems the easiest to consistently implement/
describe too. There seem to be lots of instances where elements would
belong to different (perhaps nested, e.g. field extensions)
mathematical objects and I think such elements should still be
comparable and possibly equal.

David Harvey

unread,
Oct 16, 2006, 2:43:10 PM10/16/06
to sage-...@googlegroups.com

On Mon, 16 Oct 2006, Robert Bradshaw wrote:

> Just to add my two cents, this seems natural to me, and the coercion
> definition of equality seems the easiest to consistently implement/
> describe too. There seem to be lots of instances where elements would
> belong to different (perhaps nested, e.g. field extensions)
> mathematical objects and I think such elements should still be
> comparable and possibly equal.

The nested field extensions is an excellent example.

David

Reply all
Reply to author
Forward
0 new messages