QQbar(-1)^(1/3) != AA(-1)^(1/3)

426 views
Skip to first unread message

Kwankyu Lee

unread,
Aug 27, 2024, 8:37:08 AM8/27/24
to sage-devel
Hi,

I need advice from algebraic fields experts. Currently,

sage: RR(-1)^(1/3) 
0.500000000000000 + 0.866025403784439*I 
sage: CC(-1)^(1/3) 
0.500000000000000 + 0.866025403784439*I 
sage: QQbar(-1)^(1/3) 
0.500000000000000? + 0.866025403784439?*I 
sage: AA(-1)^(1/3)                             # inconsistent 
-1

The behavior of AA(-1)^(1/3) is inconsistent. Is this intentional? Should we change the behavior?

Kwankyu Lee

unread,
Aug 27, 2024, 8:38:17 AM8/27/24
to sage-devel

Vincent Delecroix

unread,
Aug 27, 2024, 8:42:55 AM8/27/24
to sage-...@googlegroups.com
This is indeed annoying. For further input, notice that python gives
the complex principal root rather than the real root

>>> (-1.) ** 0.33
(0.5090414157503712+0.8607420270039436j)

It seems most likely that the behavior of AA should be changed.

On Tue, 27 Aug 2024 at 14:38, Kwankyu Lee <ekwa...@gmail.com> wrote:
>
> For the context, visit here: https://github.com/sagemath/sage/pull/38362#issuecomment-2311850490
>
> --
> You received this message because you are subscribed to the Google Groups "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/f73cbb85-8539-4a48-8b1d-b3b872d6b9f3n%40googlegroups.com.

Kwankyu Lee

unread,
Aug 27, 2024, 9:55:13 AM8/27/24
to sage-devel
Thanks. 

We plan to fix the behaviors in https://github.com/sagemath/sage/pull/38564, after a deprecation period.

Georgi Guninski

unread,
Aug 27, 2024, 11:04:19 AM8/27/24
to sage-...@googlegroups.com
On Tue, Aug 27, 2024 at 3:37 PM Kwankyu Lee <ekwa...@gmail.com> wrote:
>
> sage: RR(-1)^(1/3)
> 0.500000000000000 + 0.866025403784439*I

I think it is of doubtful correctness RR not being closed under coercion:
sage: RR(RR(-1)^(1/3))
TypeError: unable to convert '0.500000000000000+0.866025403784439*I'
to a real number

Nils Bruin

unread,
Aug 27, 2024, 11:56:35 AM8/27/24
to sage-devel
On Tuesday 27 August 2024 at 05:42:55 UTC-7 vdelecroix wrote:
This is indeed annoying. For further input, notice that python gives
the complex principal root rather than the real root

>>> (-1.) ** 0.33
(0.5090414157503712+0.8607420270039436j)
 
I'm not sure that's a completely fair comparison. Here you're really looking at raising a float to a float power, whereas in the the question was about raising a (negative real) algebraic number to a fractional power with odd denominator.

However, the fact that Qbar( (AA(-1))^(1/3)) != (Qbar( (AA(-1)))^(1/3) is problematic, I think. Add to that that currently even

RR(AA(-1)^(1/3)) != (RR(AA(-1))^(1/3)

(which is undoubtedly a corollary of the design decision in python) and I think there's a good reason to change. It's just hard to find analogous situations in python/sage where another branch choice is made.

There's a question of parent, though:
RR(3)^(1/3).parent() != RR(-3)!(1/3).parent(), so on real fields, raising to a power is treated differently between positive and negative bases. It would stand to reason that we do keep doing that on real algebraics.

Kwankyu Lee

unread,
Aug 27, 2024, 9:46:29 PM8/27/24
to sage-devel
I think it is of doubtful correctness RR not being closed under coercion:
sage: RR(RR(-1)^(1/3))
TypeError: unable to convert '0.500000000000000+0.866025403784439*I'
to a real number

"closed under coercion" sounds vague to me. I doubt if there is such a concept in sage.

Fractional powers not being closed is a python thing, which is inherited to sage:

>>> (-1)**(1/3)
(0.5000000000000001+0.8660254037844386j) 

Vincent Delecroix

unread,
Aug 28, 2024, 2:10:46 AM8/28/24
to sage-...@googlegroups.com
Your example illustrates twice that an operation can lead to a bigger
set : 1/3 is a python float :-)

Kwankyu Lee

unread,
Aug 28, 2024, 6:51:13 AM8/28/24
to sage-devel
> >>> (-1)**(1/3)
> (0.5000000000000001+0.8660254037844386j)

Your example illustrates twice that an operation can lead to a bigger
set : 1/3 is a python float :-)

It is puzzling, but it does not seem to be so.

If 1/3 would be a python float, (-1)**(1/3) is not defined under the usual definition of x^y = exp(y*ln(x)) with the standard branch cut for the logarithmic function. 

Python seems to deal with (-1)**(1/3) as a fractional power, and thus gives the primitive cube root of -1 as a complex number. 

Python doc says math.pow(-1, 1/3) converts 1/3 to a python float, and raises an error, unlike (-1)**(1/3). Thus math.pow seems to use the usual definition of x^y.

John Cremona

unread,
Aug 28, 2024, 6:57:43 AM8/28/24
to sage-devel
Surely the output of -1 for AA(-1)^(1/3) is correct:  AA is the "Algebraic Real Field" and -1 has exactly one cube root in there, namely itself.  On the other hand, QQbar(-1) has 3 cube roots and one is chosen (in some deterministic way).

I do not think that AA(-1)^(1/3) should return a cubroot in another field/parent when there is one in the same field/parent.  Compare

sage: QQ(-1).nth_root(3)
-1
sage: RR(-1).nth_root(3)
-1.00000000000000
sage: CC(-1).nth_root(3)
0.500000000000000 + 0.866025403784439*I

which is as it should be (in my opinion!)

John 

Kwankyu Lee

unread,
Aug 28, 2024, 7:20:02 AM8/28/24
to sage-devel
It is puzzling, but it does not seem to be so.

If 1/3 would be a python float, (-1)**(1/3) is not defined under the usual definition of x^y = exp(y*ln(x)) with the standard branch cut for the logarithmic function. 

If Python includes the negative axis in the domain of ln(x), and defines ln(x) = |x| + pi * j, then (-1)**(1/3) indeed gives the primitive cube root under the usual definition. So 1/3 is just a float in python. This interpretation seems right, since Python doesn't know fractions, a fact that I forgot.

Kwankyu Lee

unread,
Aug 28, 2024, 7:28:54 AM8/28/24
to sage-devel
On Wednesday, August 28, 2024 at 7:57:43 PM UTC+9 john.c...@gmail.com wrote:
Surely the output of -1 for AA(-1)^(1/3) is correct:  AA is the "Algebraic Real Field" and -1 has exactly one cube root in there, namely itself.  On the other hand, QQbar(-1) has 3 cube roots and one is chosen (in some deterministic way).

I do not think that AA(-1)^(1/3) should return a cubroot in another field/parent when there is one in the same field/parent.  Compare

sage: QQ(-1).nth_root(3)
-1
sage: RR(-1).nth_root(3)
-1.00000000000000
sage: CC(-1).nth_root(3)
0.500000000000000 + 0.866025403784439*I

which is as it should be (in my opinion!)

x^(1/n) and x.nth_root(n) do not behave in the same way. All x.nth_root(n) gives an n-th  root in the same field to which x belongs while all x^(1/n) gives the primitive n-th root of unity, with the exception of AA(-1)^(1/n).

 
 

Kwankyu Lee

unread,
Aug 28, 2024, 7:40:49 AM8/28/24
to sage-devel
sage: ZZ(-1).nth_root(3)
-1
sage: _.parent()
Integer Ring
sage: QQ(-1).nth_root(3)
-1
sage: _.parent()
Rational Field
sage: RR(-1).nth_root(3)
-1.00000000000000
sage: _.parent()
Real Field with 53 bits of precision

sage: CC(-1).nth_root(3)
0.500000000000000 + 0.866025403784439*I
sage: _.parent()
Complex Field with 53 bits of precision
sage: QQbar(-1).nth_root(3)
0.500000000000000? + 0.866025403784439?*I
sage: _.parent()
Algebraic Field
sage: AA(-1).nth_root(3)
-1
sage: _.parent()
Algebraic Real Field

sage: ZZ(-1)^(1/3)
(-1)^(1/3)
sage: QQ(-1)^(1/3)
(-1)^(1/3)

sage: RR(-1)^(1/3)
0.500000000000000 + 0.866025403784439*I
sage: CC(-1)^(1/3)
0.500000000000000 + 0.866025403784439*I
sage: QQbar(-1)^(1/3)
0.500000000000000? + 0.866025403784439?*I
sage: QQbar(ZZ(-1)^(1/3))
0.500000000000000? + 0.866025403784439?*I
sage: QQbar(QQ(-1)^(1/3))
0.500000000000000? + 0.866025403784439?*I
sage: AA(-1)^(1/3)
-1

Kwankyu Lee

unread,
Aug 28, 2024, 7:46:25 AM8/28/24
to sage-devel
[fowarded from Dima]

>
>x^(1/n) and x.nth_root(n) do not behave in the same way. All x.nth_root(n)
>gives an n-th  root in the same field to which x belongs while all x^(1/n)
>gives the primitive n-th root of unity, with the exception of AA(-1)^(1/n).

the function 1/n : AA->AA is 1-1 on all AA, and I don't see why this property should be broken.
Mathematically it makes perfect sense as is.

In fact, it should also work like this for any real field, not only AA.

Dima

Kwankyu Lee

unread,
Aug 28, 2024, 8:58:03 AM8/28/24
to sage-devel
[forwarded from Dima]

This looks like RR should be fixed to match AA here.

Georgi Guninski

unread,
Aug 28, 2024, 11:06:11 AM8/28/24
to sage-...@googlegroups.com
All of the following return the complex branch:
pari/gp, mpmath, maxima

Dima Pasechnik

unread,
Aug 28, 2024, 11:39:04 AM8/28/24
to Kwankyu Lee, sage-devel

Dima Pasechnik

unread,
Aug 28, 2024, 11:39:15 AM8/28/24
to Kwankyu Lee, sage-devel
This looks like RR should be fixed to match AA here.


Dima Pasechnik

unread,
Aug 29, 2024, 11:46:54 AM8/29/24
to sage-...@googlegroups.com, Georgi Guninski


On 28 August 2024 16:05:43 BST, Georgi Guninski <ggun...@gmail.com> wrote:
>All of the following return the complex branch:
>pari/gp, mpmath, maxima
>

This is just them lacking functionality specifically for real fields, not something to copy.

One can always coerse a real field element to an extension containing sqrt(-1), if there is a need for a primitive element.

Dima

Marc Mezzarobba

unread,
Aug 30, 2024, 2:20:08 AM8/30/24
to sage-...@googlegroups.com
Dima Pasechnik wrote:
> the function 1/n : AA->AA is 1-1 on all AA, and I don't see why this
> property should be broken. Mathematically it makes perfect sense as
> is.

Because the ** operator is part of the coercion system, and AA coerces
into QQbar, CC, etc.

> In fact, it should also work like this for any real field, not only
> AA.

I agree that there should be an internal nth root method, but I don't
think it can be the same as the standard exponentiation operator
without leading to all kinds of inconsistencies.

--
Marc

Dima Pasechnik

unread,
Aug 30, 2024, 4:14:49 AM8/30/24
to sage-...@googlegroups.com, Marc Mezzarobba
On Fri, Aug 30, 2024 at 7:20 AM Marc Mezzarobba <ma...@mezzarobba.net> wrote:
>
> Dima Pasechnik wrote:
> > the function 1/n : AA->AA is 1-1 on all AA, and I don't see why this
> > property should be broken. Mathematically it makes perfect sense as
> > is.
>
> Because the ** operator is part of the coercion system, and AA coerces
> into QQbar, CC, etc.

To do this is to hate the user.

Is it documented anywhere that '**' with a rational exponent p/q must
lift its operand into
a q-cyclic extension of the ground field and return a primitive
element of the corresponding extension?
IMHO no, it's just an internal implementation detail.
And should be hidden from the user, by default at least.

Similarly if AA is replaced with cyclotomics, one certainly wants the
image of an exponentiation to a rational power
to be cylcotomic, if possible, and don't always get an element in a
completely unrelated to anything general extension field.

Dima

>
> > In fact, it should also work like this for any real field, not only
> > AA.
>
> I agree that there should be an internal nth root method, but I don't
> think it can be the same as the standard exponentiation operator
> without leading to all kinds of inconsistencies.
>
> --
> Marc
>
> --
> You received this message because you are subscribed to the Google Groups "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/varoab%24dg5%241%40ciao.gmane.io.

Kwankyu Lee

unread,
Aug 30, 2024, 4:17:20 AM8/30/24
to sage-devel
I agree that there should be an internal nth root method, 

There is. It is x.nth_root(n).


Now the PR https://github.com/sagemath/sage/pull/38362 adding deprecation warnings to the use of AA(x)^(1/n) to get the real root is ready.

After one year deprecation period, the PR https://github.com/sagemath/sage/pull/38564 will change the behavior of AA(x)^(1/n) as promised.

Please look around both PRs. If there is no objection for #38362, I will set it to positive review soon. 

If anyone has objections, please state them in #38362. 


 

Kwankyu Lee

unread,
Aug 30, 2024, 4:52:58 AM8/30/24
to sage-devel
If anyone has objections, please state them in #38362. 

#38362 already became a disputed PR. 

Please come around and vote! 

Vincent Delecroix

unread,
Aug 30, 2024, 4:53:15 AM8/30/24
to sage-...@googlegroups.com
Let me mention that this question of powering is intimately related to
the question whether the powering operator should be part of the
coercion model. This has been done by Jeroen Demeyer years ago in 2017
(see https://github.com/sagemath/sage/issues/24247). Unless we revert
this, the parent of a**b should only depend on parent(a) and parent(b)
(whatever values have a and b).

To my mind, if there is a coercion between parents f : A -> B (such as
ZZ -> QQ or QQ -> AA or AA -> QQbar), it should be the case that f(a1)
** f(a2) is identical (ie value and parent) to f(a1 ** a2) whenever a1
and a2 have parents A.
> --
> You received this message because you are subscribed to the Google Groups "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/10c6230e-fd69-4a7f-a7f4-ac3203ab26e1n%40googlegroups.com.

Vincent Delecroix

unread,
Aug 30, 2024, 5:10:58 AM8/30/24
to sage-...@googlegroups.com
To complete my previous message, note that integers do not comply to
coercion because of

sage: parent(4**(1/2))
Integer Ring
sage: parent((-1)**(1/3))
Symbolic Ring

To my mind, the above is a bug.

Secondly, I am very unhappy as a user with the following

sage: AA((-1)**(1/3))
-1
sage: QQbar((-1)**(1/3))
0.500000000000000? + 0.866025403784439?*I

I would expect that the symbolic value (-1)**(1/3) to be something one
can rely on to be meaningful and coincide with the given numerical
approximation (which is the principal root)

sage: ((-1)**(1/3)).numerical_approx()
0.500000000000000 + 0.866025403784439*I

Best
Vincent
> --
> You received this message because you are subscribed to the Google Groups "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/7de8c1ba-a0da-46c0-ac94-5d960cc2d081n%40googlegroups.com.

Kwankyu Lee

unread,
Aug 30, 2024, 7:54:50 AM8/30/24
to sage-devel
On Friday, August 30, 2024 at 6:10:58 PM UTC+9 vdelecroix wrote:
Secondly, I am very unhappy as a user with the following

sage: AA((-1)**(1/3))
-1
sage: QQbar((-1)**(1/3))
0.500000000000000? + 0.866025403784439?*I

This inconsistency is fixed by the proposal.

Another parent consistent with the proposal

sage: RBF(-1)^(1/3)
nan
sage: RBF((-1)^(1/3))
...
TypeError: unable to convert (-1)^(1/3) to a RealBall

Dima Pasechnik

unread,
Aug 30, 2024, 4:29:20 PM8/30/24
to sage-...@googlegroups.com
in case, numpy types don't allow taking fractional powers of negative -1,
it returns `nan`.
E.g.
In [9]: import numpy as np
In [10]: m1=np.float64(-1)
In [11]: m1**(1/3)
<ipython-input-11-e14da7a49142>:1: RuntimeWarning: invalid value
encountered in scalar power
m1**(1/3)
Out[11]: np.float64(nan)

(same with np.int*(), too)
> --
> You received this message because you are subscribed to the Google Groups "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/0085d8e3-4558-4e1f-bedd-435d47e934dfn%40googlegroups.com.

Kwankyu Lee

unread,
Aug 30, 2024, 6:48:37 PM8/30/24
to sage-devel
On Friday, August 30, 2024 at 5:53:15 PM UTC+9 vdelecroix wrote:
Let me mention that this question of powering is intimately related to
the question whether the powering operator should be part of the
coercion model. This has been done by Jeroen Demeyer years ago in 2017
(see https://github.com/sagemath/sage/issues/24247). Unless we revert
this, the parent of a**b should only depend on parent(a) and parent(b)
(whatever values have a and b).

The gist of what Jeroen Demeyer did in #24247 seems to separate out integer powering from the general case. The general case x^y is treated like binary operation between two elements in the parent. Integer powering x^n is treated as an action of n to x. Fractional powering x^(p/q) is already dealt with the same action.  

The parent of the result of x^n or x^(p/q) is determined depending on the exponent, not  only on parent(x):

sage: ZZ(-8)^(-3)
-1/512
sage: _.parent()
Rational Field

Kwankyu Lee

unread,
Sep 2, 2024, 10:19:16 PM9/2/24
to sage-devel
The PR https://github.com/sagemath/sage/pull/38362 is for adding deprecation warning to the use of AA(x)^(1/n) to get the real root.
The PR https://github.com/sagemath/sage/pull/38564 will change the behavior of AA(x)^(1/n) after one year deprecation period.
Adding P(x).pow(m/n) = AA(x).nth_root(n)**m will be provided for fractional power computation in the parent P (including AA).

Voting for this change is going on https://github.com/sagemath/sage/pull/38362.

Dima Pasechnik

unread,
Sep 3, 2024, 4:09:59 AM9/3/24
to sage-devel
Would it be better to hold a vote to decide on the ways to deal with this issue, rather than on this particular PR?
Namely, the alternatives are

1) do nothing
2) bring AA in line with Python
3) bring RR in line with AA

Arguments against 2) are that it's mathematically weird, and that other projects such as numpy
are happy with their number types not blindly mimicking the choice made by Python.

My vote is for 3), as 1) leaves the inconsistency between AA and RR unresolved.

Dima

Oscar Benjamin

unread,
Sep 3, 2024, 6:07:59 AM9/3/24
to sage-...@googlegroups.com
On Tue, 3 Sept 2024 at 09:09, Dima Pasechnik <dim...@gmail.com> wrote:
>
> Would it be better to hold a vote to decide on the ways to deal with this issue, rather than on this particular PR?
> Namely, the alternatives are
>
> 1) do nothing
> 2) bring AA in line with Python
> 3) bring RR in line with AA

What does it mean to bring RR in line with AA here?

There are different ways to extend exponentiation a^b if b is not
constrained to integer values but there is only really one natural way
in the case of real and complex numbers if it should be both
continuous and single-valued. For real a and b with positive a we can
say that a^b = exp(b*log(a)). As long as you require a>0 this is
consistent with the ways that you would naturally define the operation
for integer or rational b or with a being in any subfield of the
reals.

The same definition a^b = exp(b*log(a)) extends to all complex numbers
if you choose a branch convention for log and then you can allow a to
be a negative number or generally any nonzero complex number. These
two definitions of exponentiation for real and for complex numbers are
consistent and are continuous in both a and b except at the branch
cuts.

The idea that for negative a and rational b you can say that a^b gives
a real root if the denominator of b is odd does not extend in any way
to a continuous function of b in either the reals or complex numbers.
Since RR is approximate it is not possible to implement an operation
like a^b coherently if the definition is not continuous in both a and
b.

To put this another way: you might want RR(-1)^(1/3) to be real but
you certainly don't want RR(-1)^(1/2) to be real. If the exponent in
a^b is an element of RR then you would need to distinguish whether it
has an even or an odd denominator to be able to say whether the root
is real or imaginary. Every non-integer element of RR literally has an
even denominator though:

>>> RR(1/3).sign_mantissa_exponent()
(1, 6004799503160661, -54)
>>> 6004799503160661 / 2**54
0.3333333333333333

Defining a^b for RR so that (-1)^(1/3) = -1 could only work if b was
restricted to the rationals so that the exponent cannot be an element
of RR. For AA the exponent is restricted to the rationals:

>>> AA(4)^(1/2)
2
>>> AA(4)^AA(1/2)
TypeError: unsupported operand parent(s) for ^:
'Algebraic Real Field' and 'Algebraic Real Field'

--
Oscar

Dima Pasechnik

unread,
Sep 3, 2024, 11:53:03 AM9/3/24
to sage-...@googlegroups.com
On Tue, Sep 3, 2024 at 11:07 AM Oscar Benjamin
<oscar.j....@gmail.com> wrote:
>
> On Tue, 3 Sept 2024 at 09:09, Dima Pasechnik <dim...@gmail.com> wrote:
> >
> > Would it be better to hold a vote to decide on the ways to deal with this issue, rather than on this particular PR?
> > Namely, the alternatives are
> >
> > 1) do nothing
> > 2) bring AA in line with Python
> > 3) bring RR in line with AA
>
> What does it mean to bring RR in line with AA here?
So that RR(-1)**(1/3)==-1, too.

>
> There are different ways to extend exponentiation a^b if b is not
> constrained to integer values but there is only really one natural way
> in the case of real and complex numbers if it should be both
> continuous and single-valued. For real a and b with positive a we can
> say that a^b = exp(b*log(a)). As long as you require a>0 this is
> consistent with the ways that you would naturally define the operation
> for integer or rational b or with a being in any subfield of the
> reals.

the point is different - on the reals the function x->x^(1/3) is 1-1.
That in Python one has non-real value for (-1)**(1/3) is
two things: 1/3 is actually a float, and absence of typing.
These are artifacts of the programming language, and make little sense
mathematically.
> --
> You received this message because you are subscribed to the Google Groups "sage-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sage-devel+...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/CAHVvXxSfnmMhcP9esrXHEZKeMi_3qwm6%3DtKkJn0JzCNN5wN%3DiA%40mail.gmail.com.

Kwankyu Lee

unread,
Sep 3, 2024, 12:08:06 PM9/3/24
to sage-devel

That in Python one has non-real value for (-1)**(1/3) is
two things: 1/3 is actually a float, and absence of typing.
These are artifacts of the programming language, and make little sense
mathematically.

Do you mean that the value of (-1)**(1/3) is arbitrarily chosen, regardless of the mathematical value of (-1)^(1/3)? 

Nils Bruin

unread,
Sep 3, 2024, 2:04:52 PM9/3/24
to sage-devel
The thing is: in Python you cannot *ask* for (-1)^(1/3) because you don't get to pass in 1/3. The function evaluated here really is

exp(log(x)*y), with x=-1 and y=6004799503160661/2^54 [where it's left to python to choose a branch for log(x) ]

Because of the duck typing conventions in python, they also cannot really make a distinction between -1 the integer, -1 the real number and -1 the complex number. So they have to settle for one unifying definition.

In sage we actually do have rational numbers that can be used for exponents. That's important because in Qbar and AA we can choose a result for raising things to a fractional power, but not for raising elements to a float/real power in general.  We already have precedent for the parent being relevant for the result. For instance GF(5)(3)^(-1) and QQ(3)^(-1) (but GF(5)(1/3) does agree, so at least we have that)

Incidentally, (GF(5)(-1))^(1/2) leads to an error.

Dima Pasechnik

unread,
Sep 3, 2024, 2:05:19 PM9/3/24
to sage-...@googlegroups.com
no, I mean that

1) 1/3 gets converted to a float, and then you cannot escape dealing
with (-1)**(0.333333...3), which is
not equal to ((-1)**(0.333333...3))**3, unlike exact (-1)**(1/3).
>>> a=(-1)**(1/3); a
(0.5000000000000001+0.8660254037844386j)
>>> a**3
(-1+3.885780586188048e-16j)

2) Python used to be untyped, and still is, to an extent, so returning
a complex number instead of real wasn't
such a big deal.

Kwankyu Lee

unread,
Sep 3, 2024, 10:44:43 PM9/3/24
to sage-devel
I agree that python language could not define (-1)**(1/3) as the real cube root of -1.

Hence it chose (-1)**(1/3) to return the complex principal root of -1, yes by computing exp(log(x)*y) with branch cut negative axis, which makes a perfect mathematical sense.

Now back in sage, sage chose to be compatible with python's behavior in x^(1/3) for whatever x that represents a number in the complex field, say x = -1, RR(-1), CC(-1), QQbar(-1).  For x = ZZ(-1), QQ(-1), they chose to return symbolic expression (-1)^(1/3), which again converted to the same value with QQbar(-1)^(1/3). For x = RBF(-1), it chose to raise an error. For x = RDF(-1), it chose to return "nan". All these suggest to sage users that x^(1/3) means the principal cube root of the number x.

Now AA(-1)^(1/3) returns -1. So AA made a different choice. There is no mathematical definition that applies to AA(-1)^(1/n). Is it n-th root in AA? Then look AA(-1)^(1/4) = 0.7071067811865475? + 0.7071067811865475?*I . Is it the principal root? Then look AA(-1)^(1/3) = -1. It is AA that is an artifact of mathematical programming language.

I think that only valid argument for AA(-1)^(1/3) = -1 is that it is the status quo. It is just human that likes what she/he used to. Removing inconsistency is for the future.


Kwankyu  




Nils Bruin

unread,
Sep 3, 2024, 11:22:33 PM9/3/24
to sage-devel
On Tuesday 3 September 2024 at 19:44:43 UTC-7 Kwankyu Lee wrote:
I think that only valid argument for AA(-1)^(1/3) = -1 is that it is the status quo. It is just human that likes what she/he used to. Removing inconsistency is for the future.

The fact that powers with odd denominator do not leave AA is also a documented design decision:



There is good precedent that the parent of a power depends on the value of the exponent. For instance, parent(3^2) is ZZ and parent(3^-1) is QQ. So I'd add as a valid argument for the branch choice in AA that it makes sense to choose a branch that does *not* require an extension of the parent, if possible. Yes, it's a different branch choice than the one made in Qbar, but AA and Qbar are different objects, with AA coercible into Qbar.

Once again, the design decisions for powering for floats in Python are made in the absence of representable non-integer rationals.

OTOH, it seems the coercion system may force our hand here (at least I think what happens below is a consequence of an overeager coercion system):

sage: parent(AA(-2)^(1/3))
Algebraic Real Field
sage: a=AA(2)^(1/2)
sage: a^a

TypeError: unsupported operand parent(s) for ^: 'Algebraic Real Field' and 'Algebraic Real Field'

sage: parent(AA(-2)^(0.3333333))

Complex Field with 53 bits of precision
sage: parent(AA(-2)^RealField(1000)(1/3))
Complex Field with 1000 bits of precision

I don't think there are many people who would be unhappy if the last two would raise an error, but here we are: algebraic numbers CAN be raised to "non-rational" exponents (represented as floats) and the result is a floating point result, with the precision gleaned from the exponent. So there IS a continuity concept for the exponent when the base is an element of AA. Unfortunately, by that argument, we're forced to take the principal branch.

I'd much rather see that exponentiation on AA with a not-explicitly-rational exponent would yield an error on account of the result not being (reliably) algebraic. Then there's not an obvious topology on QQ that needs to be respected with continuity for exponentiation and then it would be reasonable to choose a branch that does not need a larger parent. But curtailing the mechanics of coercion around exponentiation would be an even bigger change.
 

Kwankyu Lee

unread,
Sep 3, 2024, 11:52:04 PM9/3/24
to sage-devel
On Wednesday, September 4, 2024 at 12:22:33 PM UTC+9 Nils Bruin wrote:
On Tuesday 3 September 2024 at 19:44:43 UTC-7 Kwankyu Lee wrote:
I think that only valid argument for AA(-1)^(1/3) = -1 is that it is the status quo. It is just human that likes what she/he used to. Removing inconsistency is for the future.

The fact that powers with odd denominator do not leave AA is also a documented design decision:

Anything that was deprecated in sage had been documented, of course. If something is changed, the documentation also changes. 

... I'd add as a valid argument for the branch choice in AA that it makes sense to choose a branch that does *not* require an extension of the parent, if possible. Yes, it's a different branch choice than the one made in Qbar, but AA and Qbar are different objects, with AA coercible into Qbar.

If you mean "branch choice" in mathematical sense, the branch choice of Qbar (and CC and RR)

ln(z) = ln(r) + i * theta for z = r * exp(i * theta) with r > 0,  -pi < theta <= pi

(note <= at the end) is most natural for maximal continuity. I think this is more mathematical than choosing a "branch" that "does not require an extension of the parent".

Nils Bruin

unread,
Sep 4, 2024, 12:41:30 AM9/4/24
to sage-devel
On Tuesday 3 September 2024 at 20:52:04 UTC-7 Kwankyu Lee wrote:
If you mean "branch choice" in mathematical sense, the branch choice of Qbar (and CC and RR)

ln(z) = ln(r) + i * theta for z = r * exp(i * theta) with r > 0,  -pi < theta <= pi

(note <= at the end) is most natural for maximal continuity. I think this is more mathematical than choosing a "branch" that "does not require an extension of the parent".

I think the opinion about what is "mathematical" is where the difference lies here and it may well depend on whether you're more algebraically or analytically oriented. As an algebraist, one does not tend to think of Qbar or AA explicitly embedded in CC, so there is no natural extension of exponents beyond QQ. Hence, QQ does not come with a particular topology on it, so there is no continuity to consider. Exponentiation on Qbar embedded in the p-adic C_p would work out *very* differently.

For many (most?) applications of Qbar, its embedding into CC is only used to tell conjugates apart. Indeed, Magma's algebraic closure of QQ does not use an embedding into CC but instead tracks conjugate labels using a large finite field.

Kwankyu Lee

unread,
Sep 4, 2024, 2:26:00 AM9/4/24
to sage-devel


On Wednesday, September 4, 2024 at 1:41:30 PM UTC+9 Nils Bruin wrote:
.. Indeed, Magma's algebraic closure of QQ does not use an embedding into CC but instead tracks conjugate labels using a large finite field.

You mean a large number field? 

From QQbar doc:

As with many other implementations of the algebraic numbers, we try
hard to avoid computing a number field and working in the number
field; instead, we use floating-point interval arithmetic whenever
possible (basically whenever we need to prove non-equalities), and
resort to symbolic computation only as needed (basically to prove
equalities).   

Thus QQbar assumes a complex embedding from the ground, perhaps unlike Magma.

John Cremona

unread,
Sep 4, 2024, 2:53:33 AM9/4/24
to sage-...@googlegroups.com
Indeed, Magma's AlgebraicallyClosedField is not embedded into CC; it also has no version of AA.  And Nils meant what he said about large *finite* fields being used to keep track.  I find QQbar easier to use -- but when Magma's AlgebraicallyClosedField was first developed it was a really original idea and state of the art, there is at least one paper describing how it works (Nils, I cannot remember the author -- is it Allan Steele?).

John

--
You received this message because you are subscribed to a topic in the Google Groups "sage-devel" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/sage-devel/s81ieq2vpVo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to sage-devel+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/sage-devel/afb486c5-7efb-4b6c-94f9-dc230a1108fen%40googlegroups.com.

Nils Bruin

unread,
Sep 4, 2024, 3:25:58 AM9/4/24
to sage-devel
On Tuesday 3 September 2024 at 23:53:33 UTC-7 john wrote:
Indeed, Magma's AlgebraicallyClosedField is not embedded into CC; it also has no version of AA.  And Nils meant what he said about large *finite* fields being used to keep track.  I find QQbar easier to use -- but when Magma's AlgebraicallyClosedField was first developed it was a really original idea and state of the art, there is at least one paper describing how it works (Nils, I cannot remember the author -- is it Allan Steele?).

Yes, Allan Steel. ANTS 2002:
Super elegant idea. And no precision issues to take care of ... until your randomly chosen 30 digit prime ends up dividing a denominator or a discriminant of one of the algebraic numbers you happen to reference.

Kwankyu Lee

unread,
Sep 4, 2024, 6:06:28 AM9/4/24
to sage-devel
On Wednesday, September 4, 2024 at 1:41:30 PM UTC+9 Nils Bruin wrote:
... it may well depend on whether you're more algebraically or analytically oriented.

Yes. That may be a way to reconcile the conflicting views.

We may explicitly introduce technical distinction of "analytical fields" and "algebraic fields". 

Analytical fields are : RR, RDF, RBF,  ..., CC
Algebraic fields are: AA, QQbar, GF, Qp, ...

1. For an element x in analytical field P,  x^(1/n) returns the principal n-th root of x in P or in CC.
2. For an element x in algebraic field A,  x^(1/n) returns an n-th root of x in A or raises an error if there is none. 

and we live happily ever after.




Oscar Benjamin

unread,
Sep 4, 2024, 6:21:13 AM9/4/24
to sage-...@googlegroups.com
Until it is discovered that coercions exist that go from algebraic
fields to analytic ones or that there are very common rings that could
be viewed as being subrings of either analytic or algebraic fields:

>>> ZZ(-8)^(1/3)
2*(-1)^(1/3)
>>> RR(ZZ(-8)^(1/3))
...
TypeError: unable to convert '1.00000000000000+1.73205080756888*I'
to a real number

This coercion also seems questionable:

>>> parent(ZZ(8)^(1/3))
Rational Field

There is no case where an element of ZZ raised to an element of QQ
will give a non-integer rational number. Probably as alluded above
exponentiation should not use a general coercion and the type of the
exponent should be treated differently from the type of the base.

--
Oscar

Kwankyu Lee

unread,
Sep 4, 2024, 7:28:09 AM9/4/24
to sage-devel
ZZ and QQ live in their own worlds (sigh).

sage: ZZ(8)^(1/3)
2
sage: ZZ(-8)^(1/3)
2*(-1)^(1/3)
sage: ZZ(1)^(1/3)
1
sage: ZZ(-1)^(1/3)
(-1)^(1/3)
sage: QQ(8)^(1/3)
2
sage: QQ(-8)^(1/3)
2*(-1)^(1/3)
sage: QQ(1)^(1/3)
1
sage: QQ(-1)^(1/3)
(-1)^(1/3) 
 
>>> RR(ZZ(-8)^(1/3))
...
TypeError: unable to convert '1.00000000000000+1.73205080756888*I'
to a real number

This shows that symbolic expression (-1)^(1/3) is understood as the complex principal root. Then AA((-1)^(1/3)) should raise an error. Currently 

sage: AA((-1)^(1/3))
-1

In the happy world, we should understand that (-1)^(1/3) gets on different values depending on the target parent.  

This coercion also seems questionable:

>>> parent(ZZ(8)^(1/3))
Rational Field

Indeed, weird.

exponentiation should not use a general coercion and the type of the
exponent should be treated differently from the type of the base.

I agree.

Dima Pasechnik

unread,
Sep 4, 2024, 11:24:15 AM9/4/24
to sage-...@googlegroups.com


On 4 September 2024 03:44:42 BST, Kwankyu Lee <ekwa...@gmail.com> wrote:
>
>
>On Wednesday, September 4, 2024 at 3:05:19 AM UTC+9 dim...@gmail.com wrote:
>
>On Tue, Sep 3, 2024 at 5:08 PM Kwankyu Lee <ekwa...@gmail.com> wrote:
>>
>>
>> That in Python one has non-real value for (-1)**(1/3) is
>> two things: 1/3 is actually a float, and absence of typing.
>> These are artifacts of the programming language, and make little sense
>> mathematically.
>>
>>
>> Do you mean that the value of (-1)**(1/3) is arbitrarily chosen,
>regardless of the mathematical value of (-1)^(1/3)?
>
>no, I mean that
>
>1) 1/3 gets converted to a float, and then you cannot escape dealing
>with (-1)**(0.333333...3), which is
>not equal to ((-1)**(0.333333...3))**3, unlike exact (-1)**(1/3).
>>>> a=(-1)**(1/3); a
>(0.5000000000000001+0.8660254037844386j)
>>>> a**3
>(-1+3.885780586188048e-16j)
>
>2) Python used to be untyped, and still is, to an extent, so returning
>a complex number instead of real wasn't
>such a big deal.
>
>
>I agree that python language could not define (-1)**(1/3) as the real cube
>root of -1.
>
>Hence it chose (-1)**(1/3) to return the complex principal root of -1, yes
>by computing exp(log(x)*y) with branch cut negative axis, *which makes a
>perfect mathematical sense.*
>
>Now back in sage, sage chose to be compatible with python's behavior in
>x^(1/3) for whatever x that represents a number in the complex field, say x
>= -1, RR(-1), CC(-1), QQbar(-1). For x = ZZ(-1), QQ(-1), they chose to
>return symbolic expression (-1)^(1/3), which again converted to the same
>value with QQbar(-1)^(1/3). For x = RBF(-1), it chose to raise an error.
>For x = RDF(-1), it chose to return "nan". All these suggest to sage users
>that x^(1/3) means the principal cube root of the number x.


and for numpy datatype users, too, it's natural to get NaN, because numpy returns NaN.


For mathematicians who do real algebra and real algebraic geometry, it's extremely unfortunate when
reality is not respected by functions which, mathematically, can respect reality. For them your "perfect mathematical sense" is not perfect at all.

Kwankyu Lee

unread,
Sep 4, 2024, 9:38:41 PM9/4/24
to sage-devel
>Now back in sage, sage chose to be compatible with python's behavior in
>x^(1/3) for whatever x that represents a number in the complex field, say x
>= -1, RR(-1), CC(-1), QQbar(-1). For x = ZZ(-1), QQ(-1), they chose to
>return symbolic expression (-1)^(1/3), which again converted to the same
>value with QQbar(-1)^(1/3). For x = RBF(-1), it chose to raise an error.
>For x = RDF(-1), it chose to return "nan". All these suggest to sage users
>that x^(1/3) means the principal cube root of the number x.

For mathematicians who do real algebra and real algebraic geometry, it's extremely unfortunate when
reality is not respected by functions which, mathematically, can respect reality. 

So you suggest that RR(-1)^(1/3), RBF(-1)^(1/3), RDF(-1)^(1/3) all should return -1 and  RR(-1)^(1/4), RBF(-1)^(1/4), RDF(-1)^(1/4) raise errors. 

That also makes perfect mathematical sense. If you believe strongly in it, you may proceed to implement that.

 
Reply all
Reply to author
Forward
0 new messages