sage: z = ZZ(2)
sage: parent(z)
Integer Ring
sage: q = Mod(2, 7)
sage: parent(q)
Ring of integers modulo 7
sage: z == q
True
This is related to...
http://sage.math.washington.edu/home/mhansen/sage/devel/sage/doc/output/html/en/developer/coding_in_python.html#the-hash-special-method
Ralf
As you can guess, I am a lot for the new coercion system. And having
the very basic embeddings done automatically is definitely useful (say
int into rationals). But there is a tight balance to maintain, and my
feeling is that Sage is really going too far currently. And I am quite
worried about this in the long run. This leads to slower membership
and equality testing, subtle errors, more complicated code to handle
all sorts of possible cases, ...
I typically write things like:
class MyParent
def bla(self, x)
assert x in self
...
I really mean that x should be an element of self; not something that
could be converted into it.
Just yesterday, I lost 1 hour around this because membership testing
for a Weyl group ultimately led to listing all the elements of the
group. Which is problematic for an infinite group. And it was not easy
to straighten this up cleanly.
In short: for < = in, if it was just me, I would only use the most
absolutely trivial coercions. And in particular avoid there all the
coercions that involve projections and not embedding (like Z -> Z/nZ)
Just my own feeling ...
Nicolas
--
Nicolas M. Thiéry "Isil" <nth...@users.sf.net>
http://Nicolas.Thiery.name/
>> Because it's really inconvenient to always have to manually cast to
>> the same parent. Imagine I have a loop.
>>
>> while a < 1:
>> [do stuff to a to make it smaller]
>>
>> Would this fail if a was not an integer. What if it started out as an
>> integer but then I divided it by something and it became a rational.
>> It's much cleaner to write the above than
>>
>> while a < a.parent()(1):
>> [...]
>>
>> There is are natural morphisms (in Sage called coercion morphism) Z -
>>> Q, Z -> Z[x], and Z -> Z/nZ that are used to equate objects across
>> parents.
>
> As you can guess, I am a lot for the new coercion system. And having
> the very basic embeddings done automatically is definitely useful (say
> int into rationals). But there is a tight balance to maintain, and my
> feeling is that Sage is really going too far currently. And I am quite
> worried about this in the long run. This leads to slower membership
> and equality testing, subtle errors, more complicated code to handle
> all sorts of possible cases, ...
Currently the rule is that if you can do arithmetic between two
elements, you can compare them. Membership code is something entirely
different.
> I typically write things like:
>
> class MyParent
> def bla(self, x)
> assert x in self
> ...
>
> I really mean that x should be an element of self; not something that
> could be converted into it.
Membership is much more lenient than coercion, for example, I would
be more worried if the following returned False
sage: 4/2 in ZZ
True
> Just yesterday, I lost 1 hour around this because membership testing
> for a Weyl group ultimately led to listing all the elements of the
> group. Which is problematic for an infinite group. And it was not easy
> to straighten this up cleanly.
I'm not sure how a more stringent default "in" or comparison operator
would have helped here.
> In short: for < = in, if it was just me, I would only use the most
> absolutely trivial coercions. And in particular avoid there all the
> coercions that involve projections and not embedding (like Z -> Z/nZ)
>
> Just my own feeling ...
I would like to avoid having a whole hierarchy of coercions, some of
which are used in some cases, others in other cases. People have
enough trouble understanding the system as it is.
- Robert
[snip]
> In short: for < = in, if it was just me, I would only use the most
> absolutely trivial coercions. And in particular avoid there all the
> coercions that involve projections and not embedding (like Z -> Z/nZ)
>
> Just my own feeling ...
> Nicolas
... and mine.
Ralf
Very mathematical... Is the imaginary i bigger or smaller than 1?
[snip]
> Membership is much more lenient than coercion, for example, I would
> be more worried if the following returned False
> sage: 4/2 in ZZ
> True
But why? ZZ(4)/ZZ(2) cannot be executed in ZZ, because ZZ is a ring and
by default has no /. ZZ(4)/ZZ(2) just stands for
"the x such that 2*x == 4", i.e. is the result of a solving process
which might fail.
If ZZ(4)/ZZ(2) gives by default a rational number than coercion back to
ZZ involves at least testing whether the (canonical) denominator is
equal to 1. That costs time.
Is it somehow possible to distinguish between library code and
interactive code? In library code, I would prefer to have coercions
*not* automatically applied. For the interactive mode autocoercion makes
sense, since anything else would bother users too much.
[snip]
> I would like to avoid having a whole hierarchy of coercions, some of
> which are used in some cases, others in other cases. People have
> enough trouble understanding the system as it is.
Well, that, of course, must be taken into account. But as Nicolas said,
applying projections by default (I think) is not a good idea.
Ralf
Ok, I am not used to it, but this seems fair enough.
> Membership code is something entirely different.
>
> > I typically write things like:
> >
> > class MyParent
> > def bla(self, x)
> > assert x in self
> > ...
> >
> > I really mean that x should be an element of self; not something that
> > could be converted into it.
>
> Membership is much more lenient than coercion, for example, I would
> be more worried if the following returned False
>
> sage: 4/2 in ZZ
> True
Good example. Yes, finding the right balance is tricky (in MuPAD 4/2
*was* an integer).
I guess it all boils down to what are the convention for membership
testing, and how much freedom one has in implementing it.
Here are some typical options:
(1) x is in P if there is an element of P that is equal to x under ==
or if x is already an element of self (this is the doc of
__contains__ in Parent; is this a fixed rule engraved in the
marble for every Parent?)
(2) x is in P if P(x) raises no error (this is the current default
implementation in Parent)
(3) x is in P if it can be coerced into P (which could be implemented
by just testing for the existence of a coercion, without actually
applying it).
(4) x is in P if x.parent() = P
(1) is non trivial to implement. In principle, the __contains__
function of ZZ should be able to handle all parents, now and in
the future, into which there is a natural embedding of ZZ.
(2) seems completely off balance to me. Being able to construct a P
from some data does not convey any mathematical link between that
data and P.
(3) sounds reasonable, at least as a widely used default
implementation. And possibly with a couple well marked exceptions
for the really common cases like 4/2 in ZZ, to be introduced on a
case by case basis when really it feels unnatural without them.
I personally lean for (4), but am ready to call myself an extremist on
this one.
> > Just yesterday, I lost 1 hour around this because membership testing
> > for a Weyl group ultimately led to listing all the elements of the
> > group. Which is problematic for an infinite group. And it was not easy
> > to straighten this up cleanly.
>
> I'm not sure how a more stringent default "in" or comparison operator
> would have helped here.
It's a long a story. But to make it short, with option (3) and with
some more stringent conversion rules, Sage would not have tried to
convert my WeylGroup(["A", 4]) element into a matrix and then into W=
WeylGroup(["A", 3, 1]) to finally compare it with all elements of W.
> > In short: for < = in, if it was just me, I would only use the most
> > absolutely trivial coercions. And in particular avoid there all the
> > coercions that involve projections and not embedding (like Z -> Z/nZ)
> I would like to avoid having a whole hierarchy of coercions, some of
> which are used in some cases, others in other cases. People have
> enough trouble understanding the system as it is.
I definitely see your point. Well, without introducing a hierarchy,
there is this natural notion of invertible coercions (strongly
connected components in the conversion graph). But that would not have
helped anyway for 4/2 in ZZ. Here it's more about a notion of partial
inverse for the canonical coercion from ZZ into QQ. It probably would
be over engineering to introduce it.
Cheers,
> Just my own feeling ...
And mine.
Ralf
+1
Florent
> In short: for < = in, if it was just me, I would only use the most
> absolutely trivial coercions. And in particular avoid there all the
> coercions that involve projections and not embedding (like Z -> Z/nZ)
+1
I think the new coercion model in Sage is much too aggressive -
especially as applied when coding. As Ralf said: perhaps it makes
sense for interactive use. Would it be possible to enable/disable some
features dynamically?
Regards,
Bill Page
I don't see any technical problem with the idea of enabling/disabling
specific kinds of coercions. One could easily add some kind of
decorator to the Python code, e.g.
@strict_coercions
...
> From the point of view of code quality, the above story about infinite
> Weyl groups sounds to me that now, that code is better than before.
> I.e. it would work seamlessly whether the coercion model would be
> strict or not-so-strict as it is now. This clearly is a sign of greater
> robustness, and of the programmer having stated more precisely
> what the code really is supposed to do (and what not).
>
> Yeah, this might render writing new code more time-consuming.
>
The point is not that it is time-consuming per se, but rather that it
is error-prone, i.e. time spent debugging versus time spent coding.
How much of the work of ensuring "correctness" do you want the type
system to do versus how much testing will be required later?
> But it is well worth the effort! I mean, as a general rule, the "sum
> of all work" is always the same. If you shift away work from "local"
> programming, this only means that the complexity of the general
> framework would increase, and more work would have to be done
> there. More precisely, one would end up in the general framework
> with a jungle of if-else-cascades to catch each and every exceptional
> case, where some "different-to-norm" behaviour is desired. In the end
> the general framework would become unmaintainable.
>
I am sorry, I do not understand what you mean by the above.
> ...
> And also a clear "please not!" from my side to any differences in the
> behaviour in "library mode" versus "interactive mode". To the contrary,
> this is, and should be, a basic principle to be relied on, that the
> behaviour is as identical as possible! I want to write code, too, but
> without such an extra dimension of complexity to be taken care of
> during testing, experimenting, and so on.
>
I am just talking about optionally disabling certain coercions in
designated segments of Python code, thus requiring/enabling the
programmer to be more explicit in certain cases and allowing the type
system to catch more potential errors.
Regards,
Bill Page.
Where did you get (2)? That's not what the implementation in Parent
does. (Actually, the implementation in Parent is trying to implement
(1), and doing a pretty good job. It boils down to "x in ZZ" if "x ==
ZZ(x)", where TypeError and ValueError exceptions are caught and
translate to False.)
I dislike the idea of exceptions (from (3)); I prefer the current
situation, where there's a general rule that applies everywhere and is
simple enough to be memorized, even if the general rule isn't exactly
what you want always. Once you start adding exceptions "on a case by
case basis", it would be really easy to get a rule that was too
complicated to memorize... and if you can't remember what a function
does, it's pretty much useless.
If you want (4), I think you should just write x.parent() == P (or if
you know that P is unique, x.parent() is P).
(This is not to say that I'm totally opposed to a change, as long as
the new rule is simple enough to memorize and more useful than the
current rule.)
>> > In short: for < = in, if it was just me, I would only use the most
>> > absolutely trivial coercions. And in particular avoid there all the
>> > coercions that involve projections and not embedding (like Z -> Z/nZ)
>
>> I would like to avoid having a whole hierarchy of coercions, some of
>> which are used in some cases, others in other cases. People have
>> enough trouble understanding the system as it is.
>
> I definitely see your point. Well, without introducing a hierarchy,
> there is this natural notion of invertible coercions (strongly
> connected components in the conversion graph). But that would not have
> helped anyway for 4/2 in ZZ. Here it's more about a notion of partial
> inverse for the canonical coercion from ZZ into QQ. It probably would
> be over engineering to introduce it.
The strongly connected components in the conversion graph are huge.
Did you mean strongly connected components in the coercion graph?
There shouldn't be any of those; a coercion should only exist A->B or
B->A, but not both. (Otherwise how do you decide what the result of
an A plus a B is?)
Again, if there were a new rule for < = in that was still simple
enough to memorize but was more useful, I'd be in favor. But I've
thought about it and haven't come up with anything.
Here's my best effort so far:
Coercions and conversions can be marked "safe" (need a better word for
this). A safe coercion/conversion does not change the mathematical or
computational meaning (need a better definition); at a minimum, a safe
coercion/conversion is invertible where it is defined. A safe
conversion need not be defined on the entire domain.
Here's some examples to hopefully clarify:
safe:
ZZ -> QQ
QQ -> ZZ
RealField(20) -> RealField(50)
ZZ -> ZZ['x']
ZZ['x'] -> ZZ
RealField(20) -> RealIntervalField(20)
unsafe:
ZZ -> GF(5)
GF(5) -> ZZ
RealField(50) -> RealField(20)
RealField(20) -> QQ
QQ -> RealField(20)
Then "x in P" means that there is a safe conversion from the parent of
x to P. If this is actually a coercion, then you don't even have to
run it; if it's a conversion, then you do have to run it, to test that
the conversion is defined on x. "x == y", "x<y" will find a safe
coercion to a common parent if one exists; otherwise it will find a
safe conversion. If a conversion is used that fails, then the
comparison is false.
I like this design better on some days than others :) I'm not sure
it's better than the current status (it fixes some of the annoyances
in the current system, but I'm not sure what new annoyances would pop
up with the new design); I'm also not convinced it's simple enough to
memorize, and I don't know how much work it would be to change.
(Changing the meaning of equality is a little scary; I'm not sure how
much code might depend on subtleties of the current definition.)
Carl
sage: K=NumberField(x^2+1, 'a'); K
Number Field in a with defining polynomial x^2 + 1
sage: a = K.0
sage: a
a
sage: a*a
-1
sage: a<1
False
sage: a>1
True
sage: 1<a
False
sage: 1>a
True
sage: version()
'Sage Version 3.3, Release Date: 2009-02-21'
Do I do something wrong or is autocoercion doing something strange here?
In fact, I would have expected an error telling me that I cannot compare
an element of K with any other thing.
Ralf
I don't think this has anything to do with coercion; it's just that
comparison on number field elements is broken. In particular, for any
two distinct number field elements a and b, a<b is False and a>b is
True.
sage: K.<a> = NumberField(x^2+1)
sage: a < 2*a
False
sage: 2*a < a
False
sage: a > 2*a
True
sage: 2*a > a
True
Carl
> > I definitely see your point. Well, without introducing a hierarchy,
> > there is this natural notion of invertible coercions (strongly
> > connected components in the conversion graph). But that would not have
> > helped anyway for 4/2 in ZZ. Here it's more about a notion of partial
> > inverse for the canonical coercion from ZZ into QQ. It probably would
> > be over engineering to introduce it.
>
> The strongly connected components in the conversion graph are huge.
> Did you mean strongly connected components in the coercion graph?
> There shouldn't be any of those; a coercion should only exist A->B or
> B->A, but not both. (Otherwise how do you decide what the result of
> an A plus a B is?)
You don't !!! I completely disagree with that. One of the *main* usage of
computer in algebraic combinatorics is to do computations in many (usually
between 5 and 10) different bases of the same algebra. The way we use to deal
with this in MuPAD-Combinat is that each basis is a particular parent, and we
set up a graph of automatic coercion for one bases to an other. That is you
consider that for each basis B, the algebra with element stored in B ( an
instance of the category AlgebraWithBasis) is a particular algebra. You use
the coercion mechanism to say that theses algebra are isomorphic.
Let me point out some typical use-case (B1, B2 and B3) are different bases:
1) I know an explicit formula for conversions B1 -> B2 and B2 -> B3. The
problem is to experiment to find an explicit formula/fast algo for B1 -> B3
2) I know the multiplication rule in B1 and the conversions B1 <-> B2, I'm
looking for an explicit rule for multiplying two B2s
3) I don't known how to multiply two B1 or two B2, I know B1 <-> B2 and I
know how to multiply a B1 by a B2.
list goes on...
> Then "x in P" means that there is a safe conversion from the parent of
> x to P. If this is actually a coercion, then you don't even have to
> run it; if it's a conversion, then you do have to run it, to test that
> the conversion is defined on x. "x == y", "x<y" will find a safe
> coercion to a common parent if one exists; otherwise it will find a
> safe conversion. If a conversion is used that fails, then the
> comparison is false.
This preceding rule is not always true: I know many counter example.
To begin with
sage: x = int(2)
sage: type(x)
<type 'int'>
Of course x has no parent:
sage: x.parent()
...
AttributeError: 'int' object has no attribute 'parent'
sage: x in ZZ
True
So your rule "there is a safe conversion from the parent of x to P" does not
specify why we should answer yes. I fill being some lawyer in pointing out
that but I think we must use extra care when using conversion/coercion from
basic type to construct elements.
Another example to show that the example is not as trivial as it seem:
sage: l = [3,2,1]
sage: l in Permutations()
True
sage: l in Partitions()
True
Cheers,
Florent
The paragraph you quoted was part of a very rough proposal for a way
that Sage's coercion might be changed in the future; it's definitely
not how it works now.
Also, the way coercion is implemented now, int(2) does have a parent;
it's just not available at the .parent() method:
sage: parent(int(2))
<type 'int'>
Carl
This sounds potentially very useful; but option 3 ("do a strict subset
of coercions/conversions, and stop otherwise") is also tricky to
implement. For instance, if you had a mode where GF(5)(3) == ZZ(3)
raised a ValueError exception, then "GF(5)(3) in ZZ" would be false in
that mode, where it's normally true; that doesn't seem like the sort
of difference that would be helpful during debugging :)
Modes that involve printing warnings don't have this problem.
Carl
> The paragraph you quoted was part of a very rough proposal for a way
> that Sage's coercion might be changed in the future; it's definitely
> not how it works now.
My apologies for missing this.
> Also, the way coercion is implemented now, int(2) does have a parent;
> it's just not available at the .parent() method:
>
> sage: parent(int(2))
> <type 'int'>
Hum !!! Is there a place where we can find the specification and if not a
description of this parent mechanism. I'm pretty sure that if one grep into
the code on can find at least some code that use those three following
different ways to ask for the parent of an object:
1) x.parent()
2) x._parent
3) parent(x)
In the end, there can be only one ;-)
I though the first one should be used and I realized it cannot be used for
int, whence my e-mail. As far as a understood 2) is an implementation details
and should not be called directly. I've never seen 3). Note that I'm quite new
to Sage and I haven read the entire source right now :-)
Cheers,
Florent
Yes, I would find this very helpful - particularly option 3.
In that case why wouldn't
GF(5)(3) in ZZ
also return a ValueError?
Regards,
Bill Page.
Because the __contains__ method on Parent (inherited by ZZ)
specifically catches ValueError and translates it to False.
This can be fixed, of course; but this is only the example I knew of.
I would worry that there would be other parts of Sage that catch
exceptions and change their behavior. (There's a parallel discussion
on almost exactly this topic, under the subject "Coercion and
exception handling".)
Carl
+1
> but option 3 ("do a strict subset
> of coercions/conversions, and stop otherwise") is also tricky to
> implement. For instance, if you had a mode where GF(5)(3) == ZZ(3)
> raised a ValueError exception, then "GF(5)(3) in ZZ" would be false in
> that mode, where it's normally true; that doesn't seem like the sort
> of difference that would be helpful during debugging :)
+1
Up to raising or not warnings, the semantic should not change when
debugging or not. Otherwise you can run into situations where you
can't debug bugs that do occur only in non debug mode ...
(note: this is different from disabling assertions checks when in on
the debug mode, which I am fine with).
> Modes that involve printing warnings don't have this problem.
+1
Oh, right. I misread the code. Good. One option that we don't have to
consider any more.
> I dislike the idea of exceptions (from (3)); I prefer the current
> situation, where there's a general rule that applies everywhere and
> is simple enough to be memorized, even if the general rule isn't
> exactly what you want always. Once you start adding exceptions "on a
> case by case basis", it would be really easy to get a rule that was
> too complicated to memorize... and if you can't remember what a
> function does, it's pretty much useless.
Yes, I also dislike exceptions. The rules should be as uniform as
possible. I was thinking really really really rare exceptions. Maybe
just that particular case 2/2 in ZZ.
> If you want (4), I think you should just write x.parent() == P (or if
> you know that P is unique, x.parent() is P).
Yup. The question is: am I allowed to do it?
> The strongly connected components in the conversion graph are huge.
> Did you mean strongly connected components in the coercion graph?
Oops, yes, sorry.
> There shouldn't be any of those; a coercion should only exist A->B
> or B->A, but not both.
See Florent's e-mail.
Hmm. I have to think about it. For the moment, the only think I am
sure of: coercions should *always* be safe.
One use case: let S is the abstract ring of symmetric functions
(sorry, my pet example) and S.p and S.e are two concrete parents for
it (corresponding to the powersum and the elementary basis).
I certainly want coercions S.p <-> S.e, and they are safe.
On the other hand, I would not be quite convinced by:
sage: S.p.one == S.e.one
True
sage: S.p.one in S.e
True
Because S.e is mathematically defined as "the ring of symmetric
functions, expanded in the elementary basis". S.p does not match this
definition.
> I like this design better on some days than others :) I'm not sure
> it's better than the current status (it fixes some of the annoyances
> in the current system, but I'm not sure what new annoyances would pop
> up with the new design); I'm also not convinced it's simple enough to
> memorize, and I don't know how much work it would be to change.
> (Changing the meaning of equality is a little scary; I'm not sure how
> much code might depend on subtleties of the current definition.)
Isn't it?
Thanks for your comments!
I'm not sure what you mean.
I think it's fine for the .foo() method on A to document that it
"requires that its argument have the same parent as self", and then do
something like:
if self.parent() is not arg.parent(): raise ValueError
at the beginning of .foo(). Is that the question?
>> Coercions and conversions can be marked "safe" (need a better word for
>> this). A safe coercion/conversion does not change the mathematical or
>> computational meaning (need a better definition); at a minimum, a safe
>> coercion/conversion is invertible where it is defined. A safe
>> conversion need not be defined on the entire domain.
...
> Hmm. I have to think about it. For the moment, the only think I am
> sure of: coercions should *always* be safe.
Does this mean you want GF(5)(3)*2 and RR(pi)*2 to fail? These
currently work due to coercions that would be unsafe according to my
definition.
Carl
The __mul__ method exported by GF(5) could accept integers as well as
elements of GF(5), i.e. rely on operator polymorphism rather than
non-strict coercion in such cases.
Regards,
Bill Page.
I presume that you do not mean to imply that this is the only reason
to have coercion. Could it be said that this is the reason why you
want non-strict (unsafe) coercions?
Regards,
Bill Page.
This is nothing to do with coercion. Coercion is what allows on to
write 1 < 3/2 < 2. On the other hand,
sage: K=NumberField(x^2+1, 'a'); K
Number Field in a with defining polynomial x^2 + 1
sage: a = K.0
sage: one = K(1)
sage: a*a
-1
sage: one < a
False
sage: one > a
True
sage: one != a
True
The issue here is that comparison is useful outside of the purely
mathematical context--for example if one wants to sort a list (for
printing or searching) or use elements in sets or as keys in
dictionaries or simply throw an error on an illegal value like 0.
Operations like these would be much more of a pain if most objects in
Sage threw errors on comparison. At the very least it's useful to be
able to say to things are not equal (e.g. pi and 2 mod 5). Also,
though some Parents (like the Integers) have an obvious ordering,
others (say a permutation group) have many possible reasonable
choices of orderings, and so we are already making an arbitrary
choice to use < and > on them anyways.
- Robert
> Dear Carl,
>
>> The paragraph you quoted was part of a very rough proposal for a way
>> that Sage's coercion might be changed in the future; it's definitely
>> not how it works now.
>
> My apologies for missing this.
>
>> Also, the way coercion is implemented now, int(2) does have a parent;
>> it's just not available at the .parent() method:
>>
>> sage: parent(int(2))
>> <type 'int'>
>
> Hum !!! Is there a place where we can find the specification and if
> not a
> description of this parent mechanism. I'm pretty sure that if one
> grep into
> the code on can find at least some code that use those three following
> different ways to ask for the parent of an object:
> 1) x.parent()
A method on elements, returning x._parent.
> 2) x._parent
The actual C attribute that stores the Parent object (much faster
access from C code than calling a Python function).
> 3) parent(x)
If x is an Element, return x._parent, else punt and return type(x)
which can at least be somewhat reasoned with.
> In the end, there can be only one ;-)
If x is an Element, they should all agree. The latter is useful when
x is not an element, and one doesn't want to worry about attribute
errors. It's also a convenient functional notation, the same way we
allow sin(x), sqrt(x), etc. as well as x.sin(), x.sqrt(), etc.
> I though the first one should be used and I realized it cannot be
> used for
> int, whence my e-mail. As far as a understood 2) is an
> implementation details
> and should not be called directly. I've never seen 3). Note that
> I'm quite new
> to Sage and I haven read the entire source right now :-)
Hope this clears things up a bit.
- Robert
>
> On Wed, Mar 11, 2009 at 12:15 PM, Georg S. Weber wrote:
>> On 11 Mrz., 14:06, Bill Page wrote:
>>
>>> I think the new coercion model in Sage is much too aggressive -
>>> especially as applied when coding. As Ralf said: perhaps it makes
>>> sense for interactive use. Would it be possible to enable/disable
>>> some
>>> features dynamically?
[...]
> I am just talking about optionally disabling certain coercions in
> designated segments of Python code, thus requiring/enabling the
> programmer to be more explicit in certain cases and allowing the type
> system to catch more potential errors.
I am not a fan of lots of different modes that change actual
behavior, it tends to make code a lot harder to follow. Widening the
rift between interactive and library code is also a bad idea--the
fact that they are so close now is certainly a large factor in the
number of developers and rate of development.
Currently there is a coercion_traceback mechanism to see what errors
were caught--it would be easy to make a verbose flag where exceptions
and/or tracebacks were printed rather than just cached.
Also, as is the topic of the other thread, I plan on making the
exceptions caught a lot tighter.
>> From the point of view of code quality, the above story about
>> infinite
>> Weyl groups sounds to me that now, that code is better than before.
>> I.e. it would work seamlessly whether the coercion model would be
>> strict or not-so-strict as it is now. This clearly is a sign of
>> greater
>> robustness, and of the programmer having stated more precisely
>> what the code really is supposed to do (and what not).
Though we only got the tail end of this story, it didn't seem like
coercion was required to run into this bug, it just happened to
expose it.
> The point is not that it is time-consuming per se, but rather that it
> is error-prone, i.e. time spent debugging versus time spent coding.
> How much of the work of ensuring "correctness" do you want the type
> system to do versus how much testing will be required later?
Short of suppressing errors (see above) are there any concrete
examples where coercion sacrificed correctness or prolonged debugging
time?
- Robert
Allowing any exceptions is going to be a slippery slope, and also a
very fuzzy rule. Are constant polynomials in their basering? Further
out on the limb, is int(1) in Z?
>> If you want (4), I think you should just write x.parent() == P (or if
>> you know that P is unique, x.parent() is P).
>
> Yup. The question is: am I allowed to do it?
You're asking if you're allowed to redefine your __contains__? I'd
rather preserve consistency. Why not just write parent(x) == P if
that's what you're trying to test? I think "x in P" should be finer
information than a relationship between parent(x) and P.
- Robert
On Mar 11, 2009, at 7:19 PM, Bill Page wrote:
> On Wed, Mar 11, 2009 at 10:13 PM, David Roe wrote:
>> On Wed, Mar 11, 2009 at 9:53 PM, Bill Page wrote:
>>>
>>> On Wed, Mar 11, 2009 at 9:08 PM, Carl Witty wrote:
>>>> ...
>>>> Does this mean you want GF(5)(3)*2 and RR(pi)*2 to fail? These
>>>> currently work due to coercions that would be unsafe according
>>>> to my
>>>> definition.
>>>>
>>>
>>> The __mul__ method exported by GF(5) could accept integers as
>>> well as
>>> elements of GF(5), i.e. rely on operator polymorphism rather than
>>> non-strict coercion in such cases.
>>
>> The reason we have coercion is so that we don't have to do this.
+10. Otherwise every element has huge if-else lists in every __add__,
__sub__, __mul__, etc. corresponding to the fixed list various
possibilities that the programer original programmer thought of at
the time, and then those who've added to it. Much better to have a
central system that one can reason with. Then the author of _mul_
only has to worry about how to actually multiply two elements of the
same kind. And it makes it much messier to handle stuff like ZZ['x']
+ 1/2.
> I presume that you do not mean to imply that this is the only reason
> to have coercion. Could it be said that this is the reason why you
> want non-strict (unsafe) coercions?
I see this as one of the primary motivations to have coercion at all,
"safe" (injective?) or not. BTW
> Here's some examples to hopefully clarify:
> RealField(20) -> RealField(50)
> RealField(20) -> RealIntervalField(20)
I would call these dangerous, as the latter implicitly has more
"information" than the former.
- Robert
No they don't:
sage: pi20.exact_rational()
411775/131072
sage: RealField(50)(pi20).exact_rational()
411775/131072
See, exactly the same information :)
Remember that in my proposal, these "safe" coercions/conversions (I
said I needed a better name!) are only used for comparisons and "in".
The effect is that since RealField(20)->RealField(50) is a safe
conversion (not a coercion!), and RealField(50)->RealField(20) is an
unsafe coercion, RealField(20)(pi)==RealField(50)(pi) would return
False. (I want this behavior a lot more often than I want the current
behavior, where this equality test gives True.)
Carl
> Does this mean you want GF(5)(3)*2 and RR(pi)*2 to fail? These
> currently work due to coercions that would be unsafe according to my
> definition.
For R(3)*2 it seems reasonable not to fail, because every ring R can
easily made into a Z-module where the above is just a shorthand for
writing R(3) + R(3). Of course the result is in R.
In general, if you have two rings R, S and a homomorphism h: R->S, then
you can make S into a left-R-algebra by defining r*s := h(r)*s if the
image of h lies in the center of S. (Note the possibility of
non-commutativity.) If you also have an appropriate k: S->R then the
question is whether r*s (maybe now defined as r*k(s)) is in R or in S.
(I don't think that h(r)*s must be equal to r*k(s) even if you disregard
being in R or S.)
What happens in Sage in such a case? Or are cycles like (h,k)
automatically detected in the coercion framework?
Ralf
I am sorry for having started this. :-)
Really?
sage: parent(1)
Integer Ring
sage: parent(a)
Number Field in a with defining polynomial x^2 + 1
Are you saying that I can compare an integer with an element of K
without coercion. Where can I find the code for this?
> Coercion is what allows on to
> write 1 < 3/2 < 2. On the other hand,
Why is 'a' so different from 3/2?
> sage: K=NumberField(x^2+1, 'a'); K
> Number Field in a with defining polynomial x^2 + 1
> sage: a = K.0
> sage: one = K(1)
> sage: a*a
> -1
> sage: one < a
> False
> sage: one > a
> True
> sage: one != a
> True
> The issue here is that comparison is useful outside of the purely
> mathematical context
Aha... that means Sage just consideres K as an ordered set, but the
order is not necessarily compatible with operations K. Of course K is
not an ordered field.
By the way... there is a bug already mentioned earlier in this thread.
sage: a>one
True
So > is not even an order on the *set* K, because a is not the same as one.
> --for example if one wants to sort a list (for
> printing or searching) or use elements in sets or as keys in
> dictionaries
OK.
> or simply throw an error on an illegal value like 0.
That has to do with a zero-test not ordering.
Ralf
I hope no one is asking you to not post on this subject (priorities
and time constraints notwithstanding)... :-(
> On Mar 11, 2009, at 7:19 PM, Bill Page wrote:
>
>> On Wed, Mar 11, 2009 at 10:13 PM, David Roe wrote:
>>> On Wed, Mar 11, 2009 at 9:53 PM, Bill Page wrote:
>>>>
>>>> On Wed, Mar 11, 2009 at 9:08 PM, Carl Witty wrote:
>>>>> ...
>>>>> Does this mean you want GF(5)(3)*2 and RR(pi)*2 to fail? These
>>>>> currently work due to coercions that would be unsafe according
>>>>> to my
>>>>> definition.
>>>>>
>>>>
>>>> The __mul__ method exported by GF(5) could accept integers as
>>>> well as
>>>> elements of GF(5), i.e. rely on operator polymorphism rather than
>>>> non-strict coercion in such cases.
>>>
>>> The reason we have coercion is so that we don't have to do this.
>
> +10. Otherwise every element has huge if-else lists in every
> __add__, __sub__, __mul__, etc. corresponding to the fixed list
> various possibilities that the programer original programmer thought
> of at the time, and then those who've added to it.
I do not understand this claim. As Ralf pointed out, there are good
(i.e. "mathematical") reasons why it makes sense to multiply elements
of GF(5) directly by integers. This has nothing to do with coercions
or any other kind of type conversion per se. It makes sense to have
this property of GF implemented locally. It would be inconvenient to
have a symbol other than * to denote this operation. Because Python is
dynamically typed, there is no alternative but to test some condition
to determine what operation to perform. Using the coercion system to
implement this kind of polymorphism moves some properties of GF into
the coercion system instead of keeping it local.
> Much better to have a central system that one can reason with. Then
> the author of _mul_ only has to worry about how to actually multiply two
> elements of the same kind.
I have nothing against the concept of a centralized system of
coercions and I certainly would not advocate replacing it with
exclusive use of operator polymorphism.
> And it makes it much messier to handle stuff like ZZ['x'] + 1/2.
>
As others have pointed out here, coercions from ZZ['x'] -> QQ['x']
should be considered safe.
>> I presume that you do not mean to imply that this is the only reason
>> to have coercion. Could it be said that this is the reason why you
>> want non-strict (unsafe) coercions?
>
> I see this as one of the primary motivations to have coercion at all,
> "safe" (injective?) or not. BTW
>
This does not make sense to me. Surely the main reason for coercions
is for the convenience of the user. We want to make it easy to write
in a notation that makes sense to the mathematician and which behaves
in a simple and predictable (i.e. "correct") manner. What Carl called
"safe" coercions are guaranteed to satisfy this criterior. Non-safe
coercions might only be necessary for more technical reasons, such as
avoiding the use of operator polymorphism as discussed above.
> ...
Regards,
Bill Page.
No, we're saying that any oddities that arise due to having a (broken,
nontransitive) ordering on K can also be seen in situations where
coercion is not involved, so they are not in any way caused by
coercion.
Carl
It's still "implemented locally"... the coercion system doesn't know
anything about GF(5) until GF(5) is created and tells the coercion
system. (Actually, it looks like GF(5) hasn't been transitioned to
the latest coercion API, so coercion doesn't know about GF(5) until
somebody actually tries to do something like GF(5)(3)*2; at which
point it will ask "GF(5), can Integers be coerced to you?")
The current system lets you also multiply elements of GF(5)['x']['y'],
MatrixSpace(GF(5)['x'], 4, 4), ..., by Integers; this would be much
harder to arrange if the information "GF(5) can be multiplied by an
Integer" was locked inside the source code of __mul__ for GF(5).
Carl
> The issue here is that comparison is useful outside of the purely
> mathematical context--for example if one wants to sort a list (for
> printing or searching) or use elements in sets or as keys in
> dictionaries or simply throw an error on an illegal value like 0.
Sure. But if python allows for it, I'd rather not to mix mathematics and
implementation detail. Is there a way to have a different order. In MuPAD we
had a function sysorder. Let me quote the doc:
1 - A unique internal order exists for almost all objects that are created
in a MuPAD session. sysorder compares two objects according to this in-
ternal order.
!! The exceptions are domains. (Note: ie: class in python language)
2 - One should not try and use the internal order to sort objects accord-
ing to specific criteria. E.g., its does not necessarily reflect the
natural ordering of numbers or strings. Further, the internal order
may differ between different MuPAD versions.
I like point 1) but implementing it relies on some feature of the memory
management (unique rep + systematic computation of some hash value).
As you can guess 2) was more a pain than anything else...
Nevertheless I retain the idea of two orders one which is mathematically sound
and the other one which is use for internal. Of course I'd rather keeping the
usual < <= notation for the mathematical one. My dream is that the internal
one is just an extension of the mathematical one when the later has no
meaning, but this is too mush asking. Ideed, the mathematical sound < can be
very time consuming to compute. Whereas for most data structure applications
you require that it is fast to compute. Is there any low level python
data structures using search trees on something equivalent ?
> Operations like these would be much more of a pain if most objects in
> Sage threw errors on comparison.
I agrees for sysorder. Not for <...
Cheers,
Florent
+1 for having < refer to mathematical orderings (so it raises an
exception where no standard mathematical ordering is defined), and for
having a separate sysorder comparison for the somewhat-rare cases when
you really want to sort.
I've think I've voted the other way in previous discussions of this
topic. I changed my mind because:
1) I've seen a lot more mathematicians be confused/annoyed by having
defined orderings between mathematically-unordered objects since then
2) I used to think that Python more-or-less required that all objects
be ordered. I don't remember where I got that idea, but it's wrong;
somebody pointed out that Python-native complex numbers are unordered.
The standard low-level Python data structures are the list and the
dictionary; neither depends on orderings. (Dictionaries are hash
tables, so they use hashes and equality.) I don't know of any
contexts where Python implicitly depends on orderings.
This has been discussed before; for example, see
http://groups.google.com/group/sage-devel/browse_thread/thread/fa1c998160d41f62/bc550ad42bc08cc0?lnk=gst#bc550ad42bc08cc0
My suggestion in that thread of using "cmp" for sysorder is possible,
but I doubt if it's a good idea... it makes it easy to make
implementation mistakes, because by default cmp() and < use the same
code.
Carl
Yes ! The clear notion is that GF(5) is a ZZ module. And I want to stress that
this has nothing to do with coercion. It is important to realize that we use
the same symbol "*" for internal product in group/ring/algebras and
action/scalar multiplication. When you want to multiply a 1000x1000 matrix by
a scalar, you don't build a 1000x1000 scalar matrix and apply the
multiplication algorithm for matrices...
Just my two cents...
Cheers,
Florent
> On Thu, Mar 12, 2009 at 12:35 AM, Robert Bradshaw wrote:
>>
>> OK, my last post on this tread for a while, I promise :).
>
> I hope no one is asking you to not post on this subject (priorities
> and time constraints notwithstanding)... :-(
No...it just always feels a bit odd when the last 4 replies to a
thread are by the same person.
>> On Mar 11, 2009, at 7:19 PM, Bill Page wrote:
>>
>>> On Wed, Mar 11, 2009 at 10:13 PM, David Roe wrote:
>>>> On Wed, Mar 11, 2009 at 9:53 PM, Bill Page wrote:
>>>>>
>>>>> On Wed, Mar 11, 2009 at 9:08 PM, Carl Witty wrote:
>>>>>> ...
>>>>>> Does this mean you want GF(5)(3)*2 and RR(pi)*2 to fail? These
>>>>>> currently work due to coercions that would be unsafe according
>>>>>> to my
>>>>>> definition.
>>>>>>
>>>>>
>>>>> The __mul__ method exported by GF(5) could accept integers as
>>>>> well as
>>>>> elements of GF(5), i.e. rely on operator polymorphism rather than
>>>>> non-strict coercion in such cases.
>>>>
>>>> The reason we have coercion is so that we don't have to do this.
>>
>> +10. Otherwise every element has huge if-else lists in every
>> __add__, __sub__, __mul__, etc. corresponding to the fixed list
>> various possibilities that the programer original programmer thought
>> of at the time, and then those who've added to it.
>
> I do not understand this claim.
As mentioned, everything can be seen as a Z-module. This would mean
that every time I implement _mul_ I would have to handle this case.
It's much simpler to let _mul_ only worry about the ring (or group,
or field...) multiplication. One can define _rmul_, _lmul_, ... for
actions.
So, to rephrase the question, does this mean that GF(5)(3) + 1 and RR
(pi) + 1 should fail?
> As Ralf pointed out, there are good
> (i.e. "mathematical") reasons why it makes sense to multiply elements
> of GF(5) directly by integers. This has nothing to do with coercions
> or any other kind of type conversion per se. It makes sense to have
> this property of GF implemented locally. It would be inconvenient to
> have a symbol other than * to denote this operation. Because Python is
> dynamically typed, there is no alternative but to test some condition
> to determine what operation to perform. Using the coercion system to
> implement this kind of polymorphism moves some properties of GF into
> the coercion system instead of keeping it local.
To clarify, the coercion system doesn't know anything about GF, it
queries parents about their properties and then does reasoning based
on that.
>> Much better to have a central system that one can reason with. Then
>> the author of _mul_ only has to worry about how to actually
>> multiply two
>> elements of the same kind.
>
> I have nothing against the concept of a centralized system of
> coercions and I certainly would not advocate replacing it with
> exclusive use of operator polymorphism.
>
>> And it makes it much messier to handle stuff like ZZ['x'] + 1/2.
>
> As others have pointed out here, coercions from ZZ['x'] -> QQ['x']
> should be considered safe.
Sorry, what I meant it would be messy to implement this in the _add_
operator.
>>> I presume that you do not mean to imply that this is the only reason
>>> to have coercion. Could it be said that this is the reason why you
>>> want non-strict (unsafe) coercions?
>>
>> I see this as one of the primary motivations to have coercion at all,
>> "safe" (injective?) or not. BTW
>
> This does not make sense to me. Surely the main reason for coercions
> is for the convenience of the user.
Yes, it's all about convenience. I was saying that arithmetic is one
of the main conveniences coercion provides to the user and programer.
> We want to make it easy to write
> in a notation that makes sense to the mathematician and which behaves
> in a simple and predictable (i.e. "correct") manner. What Carl called
> "safe" coercions are guaranteed to satisfy this criterior. Non-safe
> coercions might only be necessary for more technical reasons, such as
> avoiding the use of operator polymorphism as discussed above.
I guess "safe" is a matter of personal taste. I find
sage: GF(5)(0) == 0
True
sage: GF(5)(1) == 1
True
sage: GF(5)(-1) == -1
True
to be "safe," but it seems some people are really bothered by this
idea and would rather have to write "a == a.parent().coerce(1)"
- Robert
> On Wed, Mar 11, 2009 at 9:35 PM, Robert Bradshaw
> <robe...@math.washington.edu> wrote:
>>> Here's some examples to hopefully clarify:
>>
>>
>>> RealField(20) -> RealField(50)
>>> RealField(20) -> RealIntervalField(20)
>>
>> I would call these dangerous,
I should clarify, it'd be dangerous to use these for arithmetic.
>> as the latter implicitly has more
>> "information" than the former.
>
> No they don't:
>
> sage: pi20.exact_rational()
> 411775/131072
> sage: RealField(50)(pi20).exact_rational()
> 411775/131072
>
> See, exactly the same information :)
I get your point, but to me if a function returns
1.00000000000000000000000000 that's more "information" than if a
function returns 1.000, and 1.00000000? is more information yet.
- Robert
> I guess "safe" is a matter of personal taste. I find
>
> sage: GF(5)(0) == 0
> True
> sage: GF(5)(1) == 1
> True
> sage: GF(5)(-1) == -1
> True
>
> to be "safe," but it seems some people are really bothered by this
> idea and would rather have to write "a == a.parent().coerce(1)"
I'd rather write a.parent().one or a.parent().one() or a.parent().unit()
or... rather than to ask for coercion. IE if you are in a ring you are
supposed to have unit which can be a complicated data structure (eg 1000x1000
sparse matrix or something even worse). If it's the case, the methods which
compute it should have a cache if it's not an attribute whereas coerce clearly
can't have reasonable cache for large base ring. I don't think having coerce
do a particular thing for 0, 1 or -1 is reasonable...
Cheers,
Florent
I could actually go for this as well. Python provides richcmp (used
for < and friends) and cmp, and I think the later could be the
"sysorder" (which would hopefully be less arbitrary than a memory
address, specifically respect the mathematical notions of equality at
least) and the former the mathematical order. On the other hand, I
would rather GF(5)(1) == pi return False than raise an error. I would
also still advocate using coercions, so GF(5)(1) == 1 returns True
(though the argument that RealField(20)(pi) == RealField(50)(pi)
returns False is a good one.)
> I've think I've voted the other way in previous discussions of this
> topic. I changed my mind because:
>
> 1) I've seen a lot more mathematicians be confused/annoyed by having
> defined orderings between mathematically-unordered objects since then
>
> 2) I used to think that Python more-or-less required that all objects
> be ordered. I don't remember where I got that idea, but it's wrong;
> somebody pointed out that Python-native complex numbers are unordered.
>
> The standard low-level Python data structures are the list and the
> dictionary; neither depends on orderings. (Dictionaries are hash
> tables, so they use hashes and equality.) I don't know of any
> contexts where Python implicitly depends on orderings.
>
> This has been discussed before; for example, see
> http://groups.google.com/group/sage-devel/browse_thread/thread/
> fa1c998160d41f62/bc550ad42bc08cc0?lnk=gst#bc550ad42bc08cc0
>
> My suggestion in that thread of using "cmp" for sysorder is possible,
> but I doubt if it's a good idea... it makes it easy to make
> implementation mistakes, because by default cmp() and < use the same
> code.
This could be changed, and probably would be a good thing (though a
lot of work--I don't know how much depends on the current semantics).
The cmp code isn't the prettiest right now. But I think this is a
distinct topic than whether or not to use coercions in comparison.
- Robert
>> I guess "safe" is a matter of personal taste. I find
>>
>> sage: GF(5)(0) == 0
>> True
>> sage: GF(5)(1) == 1
>> True
>> sage: GF(5)(-1) == -1
>> True
>>
>> to be "safe," but it seems some people are really bothered by this
>> idea and would rather have to write "a == a.parent().coerce(1)"
>
> I'd rather write a.parent().one or a.parent().one() or a.parent
> ().unit()
> or... rather than to ask for coercion. IE if you are in a ring you are
> supposed to have unit which can be a complicated data structure (eg
> 1000x1000
> sparse matrix or something even worse). If it's the case, the
> methods which
> compute it should have a cache if it's not an attribute whereas
> coerce clearly
> can't have reasonable cache for large base ring.I don't think
> having coerce
> do a particular thing for 0, 1 or -1 is reasonable...
No, certainly not. But what I'm saying is that
sage: GF(5)(3) == 3
True
Seems just as natural.
- Robery
Note that "cmp" is gone in Python 3.1+, and it's illegal to compare
values of incompatible types with "<" (although == still works). This
is interesting for two reasons: if we think we're going to switch to
Python 3 eventually, then it might be a bad idea to start introducing
more dependencies on cmp; and as a style issue, we might want to look
really hard at whether sysorder should support comparing values of
incompatible types.
Here's some text quoted from "What's new in Python 3.0"
(http://docs.python.org/dev/3.0/whatsnew/3.0.html):
Ordering Comparisons
Python 3.0 has simplified the rules for ordering comparisons:
* The ordering comparison operators (<, <=, >=, >) raise a
TypeError exception when the operands don’t have a meaningful natural
ordering. Thus, expressions like 1 < '', 0 > None or len <= len are no
longer valid, and e.g. None < None raises TypeError instead of
returning False. A corollary is that sorting a heterogeneous list no
longer makes sense – all the elements must be comparable to each
other. Note that this does not apply to the == and != operators:
objects of different incomparable types always compare unequal to each
other.
* builtin.sorted() and list.sort() no longer accept the cmp
argument providing a comparison function. Use the key argument
instead. N.B. the key and reverse arguments are now “keyword-only”.
* The cmp() function should be treated as gone, and the __cmp__()
special method is no longer supported. Use __lt__() for sorting,
__eq__() with __hash__(), and other rich comparisons as needed. (If
you really need the cmp() functionality, you could use the expression
(a > b) - (a < b) as the equivalent for cmp(a, b).)
Carl
And note that if the answer to the former question is yes, you lose
this notational convenience:
sage: K.<x> = GF(5)[]
sage: 2*x^2 + 3*x + 4
2*x^2 + 3*x + 4
You would instead have to type 2*x^2 + 3*x + GF(5)(4).
Carl
> ...
> But what I'm saying is that
>
> sage: GF(5)(3) == 3
> True
>
> Seems just as natural.
>
The reason that this seems natural is that it is a rather special case
involving a simple "literal".
Does the following
sage: a = 11
sage: GF(5)(1) == a
True
seem just as "natural"? I think one might reasonably expect to write:
sage: GF(5)(1) == GF(5)(11)
True
But why doesn't the following constitute a ValueError?
sage: a=GF(2)(1); b=GF(5)(1); c==11
True
sage: a==c
True
sage: b==c
True
sage: a==b
False
----
Equality isn't even transitive! This False result could easily mask a
simple error in the coding of some algorithm.
There is a reasonable solution but it involves some rather deep
changes to Sage. Sage decides very early on (in the preparser stage?)
that the symbol 3 is going to denote an element of Integer Ring.
The reason that GF(5)(3) == 3 seems natural is because in this context
a human reader normally reserves judgment about what the 3 stands for
until the rest of the sub-expression is understood. 3 is just a
"literal". There could be several other forms of literals such as
'F12A', 0777, etc. For any parent, Sage needs a way to denote
elements. Interpreting literals like 0 and 1 as GF(5).zero_element()
and GF(5).one_element makes sense and this can be extended in a
natural manner.
Having made the "mistake" of interpreting 3 as an integer, there is
little choice but to *convert* it to GF(5)(3) when necessary but this
is not a safe coercion in general.
Regards,
Bill Page.
There are languages that still allow that to work without coercion.
What is happening below is that a literal (like 3) is just recognised as
a (integer-like) literal. What that actually means in a certain domain
is determined by a function
integer: Literal -> %
(where % here basically stands for 'this domain'). So any domain that
wants to understand integers just implements such a function. And if it
is not yet there you provide it later (see below). Note that in the
error message below, it is not complaining about the exponents. They
must be of type MachineInteger, because the function
^: (%, MachineInteger) -> %
from P requires it and MachineInteger already provides an implementation for
integer: Literal -> %
So in the end, in 2*x^3 (just by type checking) 2 is figured out to be
in P and 3 in I. And of course the 4 below is also of type P so the
addition is (P,P)->P (no coercion needed). Below x+i fails, because
there is not function
+: (P, I) -> P
Just a side remark in favour of non-dynamic typing. ;-)
Ralf
====================================================================
>aldor -gloop
Aldor
Copyright (c) 1990-2007 Aldor Software Organization Ltd (Aldor.org).
Release: Aldor(C) version 1.1.0 (trunk-23 AXL_EDIT_1_1_13_18=0) for
LINUX(glibc2.3)
Type "#int help" for more details.
%1 >> #include "algebra"
%2 >> #include "aldorinterp"
%3 >> I==>MachineInteger -- That is basically a 32bit integer type.
%4 >> import from I, String, Symbol
%5 >> F5==SmallPrimeField 5
Defined F5 @ ? == SmallPrimeField(5)
%6 >> P==SparseUnivariatePolynomial(F5, -"x")
Defined P @ ? == SparseUnivariatePolynomial(F5, - "x")
%7 >> import from F5, P
%8 >> x: P := monom
x @ P
%9 >> 2*x^3 + 2*x + 4
^.......^
[L9 C1] #1 (Error) Argument 1 of `*' did not match any possible
parameter type.
The rejected type is MachineInteger.
Expected one of:
-- F5
-- AldorInteger
-- SparseUnivariatePolynomial(F5, - "x")
%10 >> integer(lit: Literal): P == (integer(lit)$Integer)::F5::P
Defined integer @ (lit: Literal) -> P
%11 >> 2*x^3 + 2*x + 4
2*x^3 + 2*x + 4 @ SparseUnivariatePolynomial(F5, - "x")
%12 >> i: I := 4
4 @ MachineInteger
%13 >> x+i
^
[L13 C1] #1 (Error) Argument 1 of `+' did not match any possible
parameter type.
The rejected type is P.
Expected one of:
-- String
-- MachineInteger
-- SmallPrimeField(5)
Hmmm. Quite an interesting discussion. Could anyone try to make some
sort of synthesis of the different opinions expressed in this thread?
On Thu, Mar 12, 2009 at 12:18:45PM -0700, Robert Bradshaw wrote:
> As mentioned, everything can be seen as a Z-module. This would mean
> that every time I implement _mul_ I would have to handle this case.
> It's much simpler to let _mul_ only worry about the ring (or group,
> or field...) multiplication. One can define _rmul_, _lmul_, ... for
> actions.
+10
Just 2 cents:
- I would reserve _lmul_, _rmul_ ... for actions which can be
considered as some sort of multiplication by scalars, and using
another name like the one you suggested for other actions
- I see 10*bla as (potentially) involving two independent things:
coercion and multiple dispatch
For whatever it's worth, I had started writing a draft of paper on the
coercion (= implicit conversion) and multiple dispatch mechanism I had
implemented in MuPAD:
Strangely enough, I lost part of my motivation for working on this
shortly after :-)
Cheers,
Nicolas
--
Nicolas M. Thiéry "Isil" <nth...@users.sf.net>
http://Nicolas.Thiery.name/
> Dear Robert,
>
> Hmmm. Quite an interesting discussion. Could anyone try to make some
> sort of synthesis of the different opinions expressed in this thread?
>
> On Thu, Mar 12, 2009 at 12:18:45PM -0700, Robert Bradshaw wrote:
>> As mentioned, everything can be seen as a Z-module. This would mean
>> that every time I implement _mul_ I would have to handle this case.
>> It's much simpler to let _mul_ only worry about the ring (or group,
>> or field...) multiplication. One can define _rmul_, _lmul_, ... for
>> actions.
>
> +10
>
> Just 2 cents:
>
> - I would reserve _lmul_, _rmul_ ... for actions which can be
> considered as some sort of multiplication by scalars,
It is. The semantics for _rmul_ and _lmul_ are that the other element
is guaranteed to be a scalar (i.e. a member of your basering).
> and using another name like the one you suggested for other actions
>
> - I see 10*bla as (potentially) involving two independent things:
> coercion and multiple dispatch
Yep, though in my mind they're a bit more intertwined (e.g. for a \in
Z, b \in QQ[x], one can do a*b by doing a coercion then an action,
b._lmul_(QQ(a)).
> For whatever it's worth, I had started writing a draft of paper on the
> coercion (= implicit conversion) and multiple dispatch mechanism I had
> implemented in MuPAD:
>
> http://mupad-combinat.svn.sourceforge.net/viewvc/mupad-combinat/
> trunk/MuPAD-Combinat/Papers/2007-12-13-Overloading.tex?view=markup
>
> Strangely enough, I lost part of my motivation for working on this
> shortly after :-)
I'll take a look. I've been intending to write this up as a paper
too, but haven't found the time yet.
- Robert
Definitely.
> > For whatever it's worth, I had started writing a draft of paper on the
> > coercion (= implicit conversion) and multiple dispatch mechanism I had
> > implemented in MuPAD:
> >
> > http://mupad-combinat.svn.sourceforge.net/viewvc/mupad-combinat/
> > trunk/MuPAD-Combinat/Papers/2007-12-13-Overloading.tex?view=markup
> >
> > Strangely enough, I lost part of my motivation for working on this
> > shortly after :-)
>
> I'll take a look. I've been intending to write this up as a paper
> too, but haven't found the time yet.
Well, maybe we could join forces, and write a paper "coercion and
dispatch in Sage and MuPAD". Having more than one implementation of
the concept would even make it a standard :-)
Best,
That non-transitivity basically means that == is not an equivalence
function. Ehm, well, on which set anyway?
Is there a function in Sage that really behaves like mathematical equality?
Ralf
> If you think about it, this would be rather hard to implement in
> general, in terms of complexity at least.
It is easier than you think.
x==y gives true if and only if y is the same object as x (basically
memory address comparison). But, of course, then a copy of x is not
equal to x. That also does not make much sense.
However, (for me at least) it would be sufficient, if a==b returns false
if a and b have different parents. Then all boils down to implement ==
appropriately for one type (ehm, you call that parent).
Does someone see a serious problem with that?
Of course, then QQ(1)==ZZ(1) would return false. But I really don't see
a problem with that. If you want to have a more user friendly '==', then
introduce another function, let's call it EQ for the moment, so that
EQ(QQ(1), ZZ(1)) is allowed to apply coercions.
I don't care much whether you use the name '==' for the first meaning
(returning false for unequal parents) or for the second. But I'd prefer
one "equality" that does not involve coercions and another one where
implicit coercions are allowed. Actually, I don't care much about an
'equality function with implicit coercions'. It's very hard to figure
out from the source code what the programmer actually meant.
Note that it might even be a speed consideration. Always looking up
coercions (even if they are cached) is not a good strategy. And the
current '==' not being transitive might also hide bugs that are really
hard to detect.
Ralf
>
>>> Is there a function in Sage that really behaves like mathematical
>>> equality?
>
>> If you think about it, this would be rather hard to implement in
>> general, in terms of complexity at least.
>
> It is easier than you think.
>
> x==y gives true if and only if y is the same object as x (basically
> memory address comparison). But, of course, then a copy of x is not
> equal to x. That also does not make much sense.
>
> However, (for me at least) it would be sufficient, if a==b returns
> false
> if a and b have different parents. Then all boils down to implement ==
> appropriately for one type (ehm, you call that parent).
>
> Does someone see a serious problem with that?
>
> Of course, then QQ(1)==ZZ(1) would return false. But I really don't
> see
> a problem with that.
I would find that super inconvenient.
> If you want to have a more user friendly '==', then
> introduce another function, let's call it EQ for the moment, so that
> EQ(QQ(1), ZZ(1)) is allowed to apply coercions.
>
> I don't care much whether you use the name '==' for the first meaning
> (returning false for unequal parents) or for the second. But I'd
> prefer
> one "equality" that does not involve coercions and another one where
> implicit coercions are allowed. Actually, I don't care much about an
> 'equality function with implicit coercions'. It's very hard to figure
> out from the source code what the programmer actually meant.
How about "parent(a) == parent(b) and a == b"
> Note that it might even be a speed consideration. Always looking up
> coercions (even if they are cached) is not a good strategy. And the
> current '==' not being transitive might also hide bugs that are really
> hard to detect.
People have yet to show an example where this really hides any bugs.
- Robert
>
> 2009/3/13 Ralf Hemmecke <ra...@hemmecke.de>:
>
>> Is there a function in Sage that really behaves like mathematical
>> equality?
>
> If you think about it, this would be rather hard to implement in
> general, in terms of complexity at least.
Indeed, it is hard to nail down what one means by equality. For
example, is R[x] equal to R[y]. What about the commutative rings R
[x,y] and R[y,x]. What about sparse R[x] vs. dense R[x]. Do you
consider all vector spaces over K of the same dimension equal, or do
they have to have a specified basis? Nailing down questions like
these is unclear.
- Robert
As I said, different type/parent must lead to a==b returning false.
If you implement R[x] different from R[y] then no element from R[x] can
be equal to an element from R[y]. If you implement R[x] and R[y] as just
finite sequences over R then there is only one type and elements compare
as the would as finite sequences. Now whether u==v for u\in R[x] and
v\in R[y] returns true or false must clearly be written in the
specification of the domain R[.].
Ralf
> I would find that super inconvenient.
Well, maybe later you'll appreciate my suggestion a bit more.
> How about "parent(a) == parent(b) and a == b"
(of course "a==b" also must have "parent(a)==parent(b)" inside, because
that is the condition that decides whether a coercion lookup should
start or whether the == just works inside the parent. So
"parent(a)==parent(b)" has to be evaluated twice.
>> Note that it might even be a speed consideration. Always looking up
>> coercions (even if they are cached) is not a good strategy. And the
>> current '==' not being transitive might also hide bugs that are really
>> hard to detect.
> People have yet to show an example where this really hides any bugs.
Good luck for the bug search whenever this will happen.
Actually, programmers need just be a bit careless. They have tested a==b
and b==c and then use c where they should have used a. If the code is
complex enough, you can even read the code and don't spot the error. We
all are so used to the fact that something that looks like == behaves
like an equivalence relation.
Ralf
Indeed, even Python agrees:
Python 3.0 (r30:67503, Jan 23 2009, 04:39:45)
[GCC 4.2.4 (Ubuntu 4.2.4-1ubuntu3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import fractions
>>> fractions.Fraction(1)
Fraction(1, 1)
>>> fractions.Fraction(1) == 1
True
>>>
-cc
Not that this really has much to do with computer algebra or
mathematics per se, but I am curious if anyone can find a situation in
pure Python (i.e. using only the standard Python library definitions
for == ) that gives the following result:
Python 2.4.6 (#2, Dec 20 2008, 15:02:30)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> # some definition for a, b, and c
>>> ... ?
>>> a==c
True
>>> b==c
True
>>> a==b
False
>>>
Regards,
Bill Page.
I was going a bit off topic here and talking about comparing the
actual Parents R[x,y] and R[y,x], etc. not their elements, because
here the notion of == becomes much more murky.
- Robert
> On Fri, Mar 13, 2009 at 7:06 PM, Ralf Hemmecke <ra...@hemmecke.de>
> wrote:
>>
>>>> Of course, then QQ(1)==ZZ(1) would return false. But I really don't
>>>> see a problem with that.
>>
>>> I would find that super inconvenient.
>>
>> Well, maybe later you'll appreciate my suggestion a bit more.
>>
>>> How about "parent(a) == parent(b) and a == b"
>>
>> (of course "a==b" also must have "parent(a)==parent(b)" inside,
>> because
>> that is the condition that decides whether a coercion lookup should
>> start or whether the == just works inside the parent. So
>> "parent(a)==parent(b)" has to be evaluated twice.
Yes. Actually it tests parent(a) is parent(b) which is both really
fast and usually true (we try to make Parents unique), so it's not
much of an overhead.
- Robert
teragon:papers wstein$ sage -python
Python 2.5.2 (r252:60911, Mar 12 2009, 23:58:30)
[GCC 4.0.1 (Apple Inc. build 5488)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 10**22; b = 10**22+1; c = complex(a)
>>> a == c
True
>>> b == c
True
>>> a == b
False
-- William
Is it "the concept"? Did you guys independently arrive at the same solution?
-- william
Thanks*, William!
*There is some kind of odd emotionally facially expression that I
would like to convey along with the work "thanks" which means
something like: I am very glad you took the time to show me this
example, I am very surprised/shocked/disappointed by the result.
I wonder if this is a widely known and intended behaviour?
Regards,
Bill Page.
> Well, maybe we could join forces, and write a paper "coercion and
> dispatch in Sage and MuPAD". Having more than one implementation of
> the concept would even make it a standard :-)
Maybe it's even possible to include the FriCAS coercion system, too?
It has some nice sides, and some ugly ones, too.
Interest?
Martin
> Since the topic now changed into "is Sage implementing Mathematics":
> IMHO it is frankly impossible for *any* CAS to implement a
> mathematically meaningful notion of == that is both useful and
> rigorous.
This is certainly not true. The point is that you have to make equality
mean different things for different domains.
For many many many domains you can have a "mathematical equality".
Eg.,
* Integers,
* Algebraic numbers,
* Strings
* Polynomials with coefficients in a domain with mathematical
equality
* Matrices with entries in a domain with mathematical equality
* QuotientFields, where the IntegralDomain has mathematical equality
* Holonomic Functions with coefficients in a domain with mathematical
equality
* Algebraic Functions with coefficients in a domain with mathematical
equality
* Differentially Algebraic Functions with coefficients in a domain
with mathematical equality
...
Therefore, at least in my opinion, it makes sense to be able to work
in a given domain, and define == there.
I must admit that I do not understand (yet) how Sage works here, but
I thought it would define equality separately for every "parent".
Doesn't it, or did I miss something?
Martin
No, there's stuff in Sage not in MuPAD and vice-versa. However, both
are sufficiently interesting and novel.
- Robert
On Sat, Mar 14, 2009 at 1:25 PM, Simon King wrote:
> ... yes, Sage does.
>
> But I wanted to point out that in order to have a *useful* notion of
> ==, you should be able to compare, say, ZZ(1) and RR(1).
No, that is usually not the case, and as far as I understand it is not
the case in Sage. What one can do is convert ZZ -> RR in a canonical
way (aka. coercion), and then you are just comparing RR(1) to RR(1).
One does not compare ZZ(1) to RR(1) directly.
> And once you start to compare apples and oranges, sooner or later
> you can't avoid to find cases that can't be treated in a mathematically
> rigorous way.
>
There is no need to compare apples to oranges. Sometimes we can say in
what way an apple is like an orange (or vice versa) and then we can
compare oranges to oranges (or apples to apples).
>> For many many many domains you can have a "mathematical equality".
>
> Agreed. And for many many useful domains (the reals, for example, or
> the category of finitely presented groups) you can't.
>
> Let me try to put it differently:
> * "A is B" is certainly useful and (computationally) rigorous, but it
> is not a mathematical notion.
> * Any implementation of a useful (!), thorough (!!) and consistent
> mathematical notion of "==" will sooner or later show inconsistencies.
That is not true. But Godel did show that one must choose either
consistency or completeness. The more reasonable choice for any
logical or compational (algebraic) system is consistency. As a
consequence there are classes and some things are undecidable.
> Of course, there is a very consistent mathematical notion of
> ==: "A==B for all A and B" -- but this is not useful. And of course
> there is a consistent and useful notion of == restricted to the integers.
> But mathematics is more than the integers.
>
Indeed.
> The simple reason for my post was that I felt offended by the subject
> of this thread: "Is Sage implementing mathematics" -- well, of course
> it does not. I think it is a theorem that no CAS implements
> mathematics (once it reaches a certain level). But this is no show
> stopper, since still CASs are useful for doing mathematics.
>
Although at best this might be viewed as a conflict of competing
mathematical philosophies (and at worse a matter of personal
philosophies), I think the fact that you (and perhaps others) might
be "offended" by the implication of the subject of this thread shows a
rather profound misunderstanding of the relationship between computer
programming and mathematics. "Implementing mathematics" should not be
confused with the (wrong) idea that it is somehow possible to
"mechanize" all of mathematics!
Regards,
Bill Page.
For information: in MuPAD, equality is plain syntactical equality
(same type, same internal representation), and as far as I know this
has never been seriously complained about, despite users ranging from
high school student to researchers.
Testing the semantical equality of a and b is usually done by
iszero(a-b) (or some variant thereof for e.g. groups).
The only smoothie is that, as a very special exception, 6/3 returns an
integer and not a rational number, so that 6/3 = 2 is actually true.
I don't know precisely how the FriCAS coercion system works. But if it
indeed is close enough, it would make sense. And this certainly would
be a good occasion to compare them in detail and cross-polinize the
best ideas.
Do you foresee any occasion to meet physically all three of us?
Cheers,
> Did you guys independently arrive at the same solution?
As far as I know yes. My first implementation in MuPAD dates from
2003. My reimplementation in December 2007 is essentially a technical
optimization (distributed datastructure). When I first met with Robert
in San-Diego and discussed the matter, the main lines were readily the
same.
> Is it "the concept"?
I guess that once a certain level of complexity in the
coercions/overloading is reached, there are not that many options for
how a coercion system for mathematics could work in a dynamically
typed language. Also, most of the ideas were in the air (multiple
dispatch, ...).
Cheers,
> On Sat, Mar 14, 2009 at 03:51:36PM +0100, Martin Rubey wrote:
> >
> > "Nicolas M. Thiery" <Nicolas...@u-psud.fr> writes:
> >
> > > Well, maybe we could join forces, and write a paper "coercion and
> > > dispatch in Sage and MuPAD". Having more than one implementation of
> > > the concept would even make it a standard :-)
> >
> > Maybe it's even possible to include the FriCAS coercion system, too?
>
> I don't know precisely how the FriCAS coercion system works. But if it
> indeed is close enough, it would make sense. And this certainly would
> be a good occasion to compare them in detail and cross-polinize the
> best ideas.
>
> Do you foresee any occasion to meet physically all three of us?
Well, at least two of us are at FPSAC 09 at RISC.
(I love all these abbreviations, it feels so french :-)
I should admit that I did not read the whole thread... Maybe it would
be useful to start with a wiki page (possibly on MathAction, since we
can use FriCAS and Sage, and possibly even MuPAD there -- although I
guess you can do that on the Sage Wiki, too, but I don't know -- and
because it's intended use originally was to allow for cooperation
between different CAS...)
FriCAS coercion in two words:
(1) There are FriCAS-Categories (i.e., roughly classes -- "%" is FriCAS
speak for "self")
CoercibleTo(S) with coerce: % -> S,
ConvertibleTo(S) with convert: % -> S and
RetractableTo(S) with coerce: S -> % and retract: % -> S
the idea was that one can then ask
A has CoercibleTo B
and get true if this is possible and false otherwise.
Unfortunately, this was never implemented thoroughly. There is a
(stupid) reason, which takes too long to explain here
(2) in truth, most domains inherit various operations
coerce: % -> S and coerce: S -> %
from the categories (classes in OO speak) they inhering from.
the only thing special about the name coerce is that it has
syntactic sugar. s::S is short for coerce(s)@S (the "@" sign
followed by a domain "S" means: use the operation coerce that
gives me an element in S)
For example:
Algebra(R:CommutativeRing): Category ==
Join(Ring, Module R) with
--operations
coerce: R -> %
++ coerce(r) maps the ring element r to a member of the algebra.
add
coerce(x:R):% == x * 1$%
I.e., every Algebra over R exports an operation coerce: R -> %.
One can then do
(1) -> SQMATRIX(2, INT) has Algebra INT
(1) true
Type: Boolean
(2) -> SQMATRIX(2, INT) has coerce: Integer -> %
(2) true
Type: Boolean
(3) The interpreter makes a heuristic choice which signatures to
prefer over others. This algorithm works usually very very well,
although it could be still improved.
Sorry for being so short,
Martin
:-)
> (3) The interpreter makes a heuristic choice which signatures to
> prefer over others. This algorithm works usually very very well,
> although it could be still improved.
Just a short question: does the interpreter handle transitivity:
i.e. if there is a coercion A -> B and one B -> C, then deduce one for
A -> C?
"Nicolas M. Thiery" <Nicolas...@u-psud.fr> writes:
> > > Do you foresee any occasion to meet physically all three of us?
> >
> > Well, at least two of us are at FPSAC 09 at RISC.
> >
> > (I love all these abbreviations, it feels so french :-)
>
> :-)
>
> > (3) The interpreter makes a heuristic choice which signatures to
> > prefer over others. This algorithm works usually very very well,
> > although it could be still improved.
>
> Just a short question: does the interpreter handle transitivity:
> i.e. if there is a coercion A -> B and one B -> C, then deduce one for
> A -> C?
I don't think so, but I should say that my point (3) was actually a
bit cryptic. Thus, I elaborate a little (I've got two minutes):
(a) In library code, coercions (mostly) have to be explicit. I think
there are a few implicit coercions, but this is a separate issue,
I'd say, i.e., the implementation of the language is not perfect.
Library code is compiled by the SPAD compiler.
(b) To make life enjoyable, the interpreter uses heuristics, i.e., it
"guesses" types. Eg., when you enter 3, the interpreter will
answer with "PositiveInteger" and not with "Polynomial Integer"
or "PrimeField 17". The algorithm chooses a reasonably specific
type. The types it chooses are (currently) hard-coded, and I
think they need to be, at least partially.
(c) When you want to use an operation, the interpreter looks at all
available operations with this name and the right number of
arguments.
If there is an operation that fits perfectly, i.e., the type of
the argument in the signature matches the type of the argument
provided by the user, that's the winner.
Otherwise it will use (roughly) the operation with the least
number of coercions necessary to make the types match.
Alltogether, I'd say that transitivity of coerce is not used, and is
not necessary, or, alternatively, you could say that it's coded in
the library, as in the example I provided in my previous post, or in
Ring(): Category == Join(Rng,SemiRing,LeftModule(%), unitsKnown) with
[...]
coerce: Integer -> %
++ coerce(i) converts the integer i to a member of the given domain.
I.e., *every* Ring allows coercion from an Integer. Of course, the
interpreter finds this operation.
Martin
> > > Do you foresee any occasion to meet physically all three of us?
> >
> > Well, at least two of us are at FPSAC 09 at RISC.
> >
> > (I love all these abbreviations, it feels so french :-)
>
> :-)
>
> > (3) The interpreter makes a heuristic choice which signatures to
> > prefer over others. This algorithm works usually very very well,
> > although it could be still improved.
>
> Just a short question: does the interpreter handle transitivity:
> i.e. if there is a coercion A -> B and one B -> C, then deduce one for
> A -> C?
I just realized that you probably "really" are asking about how to
deal with domains that have many representations, bases, etc., like
symmetric functions. I.e., in the very simplest case you have
symmetric functions in elementary symmetric functions
... complete ...
... homogeneous ...
... forgotten ...
etc, and you want to specify some coercions, but not all
binomial(n,2).
I have not come up with a completely satisfying answer yet. One
possibility is to have a coercion package that performs the
coercion. A complete (but silly) example is attached.
The problem with this approach is that
MyDom1 has coerce: % -> MyDom2
gives false, because the coerce is not exported by MyDom1, but rather
by MyCoerce...
The good thing about this approach is that you can play all tricks
you like in MyCoerce. For example, you could easily implement
Dijkstra to find a shortest coercion path. Of course, you could also
cache this.
Is this what you had in mind?
Martin
)abb category MYCAT MyCat
MyCat(): Category == SetCategory with
0: constant -> %
1: constant -> %
"+": (%,%) -> %
"*": (NonNegativeInteger,%) -> %
coerce: % -> NonNegativeInteger
add
a * b ==
if zero? a then 0$%
else subtractIfCan(a, 1)::NonNegativeInteger * b + b
)abb domain MYDOM1 MyDom1
MyDom1(): MyCat
== add
Rep := NonNegativeInteger
0 == 0$NonNegativeInteger
1 == 1$NonNegativeInteger
coerce(a: %): NonNegativeInteger == a::Rep
coerce(a: %): OutputForm == coerce(a::Rep)$NonNegativeInteger
a + b ==
(a::Rep + b::Rep)$NonNegativeInteger
)abb domain MYDOM2 MyDom2
MyDom2(): MyCat
== add
Rep := String
0 == ""
1 == "a"
coerce(a: %): NonNegativeInteger == #(a::Rep)
coerce(a: %): OutputForm == coerce(a::Rep)$String
a + b == concat(a::Rep, b::Rep)
)abb package MYCOERCE MyCoerce
MyCoerce(A: MyCat, B: MyCat): with
coerce: A -> B
== add
coerce a == a::NonNegativeInteger * 1$B
On Mon, Mar 16, 2009 at 11:35:22AM +0100, Martin Rubey wrote:
> "Nicolas M. Thiery" <Nicolas...@u-psud.fr> writes:
> > Just a short question: does the interpreter handle transitivity:
> > i.e. if there is a coercion A -> B and one B -> C, then deduce one for
> > A -> C?
>
> I just realized that you probably "really" are asking about how to
> deal with domains that have many representations, bases, etc., like
> symmetric functions.
Definitely! The system should be flexible enough to support this,
among other things.
> I have not come up with a completely satisfying answer yet. One
> possibility is to have a coercion package that performs the
> coercion. A complete (but silly) example is attached.
>
> The problem with this approach is that
>
> MyDom1 has coerce: % -> MyDom2
>
> gives false, because the coerce is not exported by MyDom1, but rather
> by MyCoerce...
> The good thing about this approach is that you can play all tricks
> you like in MyCoerce. For example, you could easily implement
> Dijkstra to find a shortest coercion path. Of course, you could also
> cache this.
>
> Is this what you had in mind?
Yup. That's what's implemented in MuPAD (with some subtleties so as to
scale well to a couple thousand domains/parents, and in particular not
get in the way of the memory management).
The Sage implementation has the same aims.