Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change behavior of algebraic reals with respect to fractional powers #38564

Draft
wants to merge 9 commits into
base: develop
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/sage/features/sagemath.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ class sage__rings__number_field(JoinFeature):

sage: # needs sage.rings.number_field
sage: AA(-1)^(1/3)
-1
0.500000000000000? + 0.866025403784439?*I
sage: QQbar(-1)^(1/3)
0.500000000000000? + 0.866025403784439?*I

Expand Down
2 changes: 1 addition & 1 deletion src/sage/rings/number_field/number_field_element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2832,7 +2832,7 @@ cdef class NumberFieldElement(NumberFieldElement_base):
sage: AA(alpha)
Traceback (most recent call last):
...
ValueError: Cannot coerce algebraic number with nonzero imaginary
ValueError: cannot coerce algebraic number with nonzero imaginary
part to algebraic real

sage: NF.<alpha> = NumberField(x^5 + 7*x + 3)
Expand Down
72 changes: 36 additions & 36 deletions src/sage/rings/qqbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,16 @@
1
sage: QQbar((-8)^(1/3))
1.000000000000000? + 1.732050807568878?*I
sage: AA((-8)^(1/3))
-2
sage: QQbar((-4)^(1/4))
1 + 1*I
sage: AA((-8)^(1/3))
Traceback (most recent call last):
...
ValueError: cannot coerce algebraic number with nonzero imaginary part to algebraic real
sage: AA((-4)^(1/4))
Traceback (most recent call last):
...
ValueError: Cannot coerce algebraic number with nonzero imaginary part to algebraic real
ValueError: cannot coerce algebraic number with nonzero imaginary part to algebraic real

The coercion, however, goes in the other direction, since not all
symbolic expressions are algebraic numbers::
Expand All @@ -134,11 +136,10 @@
sage: QQbar(sqrt(2) + QQbar(sqrt(3))) # needs sage.symbolic
3.146264369941973?

Note the different behavior in taking roots: for ``AA`` we prefer real
roots if they exist, but for ``QQbar`` we take the principal root::
Note that both for ``AA`` and ``QQbar``, we take the principal root::

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

Expand Down Expand Up @@ -298,8 +299,8 @@
sage: n = (rt2 + rt3)^5; n
308.3018001722975?
sage: sage_input(n)
R.<x> = AA[]
v1 = AA.polynomial_root(AA.common_polynomial(x^2 - 2), RIF(RR(1.4142135623730949), RR(1.4142135623730951))) + AA.polynomial_root(AA.common_polynomial(x^2 - 3), RIF(RR(1.7320508075688772), RR(1.7320508075688774)))
R.<x> = QQbar[]
v1 = QQbar.polynomial_root(AA.common_polynomial(x^2 - 2), CIF(RIF(RR(1.4142135623730949), RR(1.4142135623730951)), RIF(RR(0)))).real() + QQbar.polynomial_root(AA.common_polynomial(x^2 - 3), CIF(RIF(RR(1.7320508075688772), RR(1.7320508075688774)), RIF(RR(0)))).real()
v2 = v1*v1
v2*v2*v1

Expand Down Expand Up @@ -335,8 +336,8 @@
sage: n = rt2^2
sage: sage_input(n, verify=True)
# Verified
R.<x> = AA[]
v = AA.polynomial_root(AA.common_polynomial(x^2 - 2), RIF(RR(1.4142135623730949), RR(1.4142135623730951)))
R.<x> = QQbar[]
v = QQbar.polynomial_root(AA.common_polynomial(x^2 - 2), CIF(RIF(RR(1.4142135623730949), RR(1.4142135623730951)), RIF(RR(0)))).real()
v*v
sage: sage_input(n, verify=True)
# Verified
Expand Down Expand Up @@ -1157,7 +1158,7 @@
if x.imag().is_zero():
return x.real()
else:
raise ValueError("Cannot coerce algebraic number with nonzero imaginary part to algebraic real")
raise ValueError("cannot coerce algebraic number with nonzero imaginary part to algebraic real")
elif hasattr(x, '_algebraic_'):
return x._algebraic_(AA)
return AlgebraicReal(x)
Expand Down Expand Up @@ -4324,9 +4325,8 @@
.. WARNING::

Note that for odd `n`, all ``False`` and negative real numbers,
``AlgebraicReal`` and ``AlgebraicNumber`` values give different
answers: ``AlgebraicReal`` values prefer real results, and
``AlgebraicNumber`` values return the principal root.
``AlgebraicReal`` and ``AlgebraicNumber`` values return the
principal root.

EXAMPLES::

Expand Down Expand Up @@ -4361,6 +4361,9 @@
True
"""
if not all:
if self.parent() is AA and self.sign() < 0:
if n % 2:
return -((-self) ** ~ZZ(n))
return self ** ~ZZ(n)
else:
root = QQbar(self) ** ~ZZ(n)
Expand Down Expand Up @@ -5101,7 +5104,7 @@
sage: QQbar.zeta(3)._mpfr_(RR)
Traceback (most recent call last):
...
ValueError: Cannot coerce algebraic number with nonzero imaginary part to algebraic real
ValueError: cannot coerce algebraic number with nonzero imaginary part to algebraic real
"""
return AA(self)._mpfr_(field)

Expand All @@ -5120,7 +5123,7 @@
sage: float(QQbar.zeta(3))
Traceback (most recent call last):
...
ValueError: Cannot coerce algebraic number with nonzero imaginary part to algebraic real
ValueError: cannot coerce algebraic number with nonzero imaginary part to algebraic real
"""
return AA(self).__float__()

Expand Down Expand Up @@ -5174,13 +5177,13 @@
sage: QQbar.zeta(6)._integer_()
Traceback (most recent call last):
...
ValueError: Cannot coerce algebraic number with nonzero imaginary part to algebraic real
ValueError: cannot coerce algebraic number with nonzero imaginary part to algebraic real

sage: # needs sage.symbolic
sage: QQbar(sqrt(17))._integer_()
Traceback (most recent call last):
...
ValueError: Cannot coerce non-integral Algebraic Real 4.123105625617660? to Integer
ValueError: cannot coerce non-integral Algebraic Real 4.123105625617660? to Integer
sage: QQbar(sqrt(16))._integer_()
4
sage: v = QQbar(1 + I*sqrt(3))^5 + QQbar(16*sqrt(3)*I); v
Expand All @@ -5203,13 +5206,13 @@
sage: (QQbar.zeta(7)^3)._rational_()
Traceback (most recent call last):
...
ValueError: Cannot coerce algebraic number with nonzero imaginary part to algebraic real
ValueError: cannot coerce algebraic number with nonzero imaginary part to algebraic real

sage: # needs sage.symbolic
sage: QQbar(sqrt(2))._rational_()
Traceback (most recent call last):
...
ValueError: Cannot coerce irrational Algebraic Real 1.414213562373095? to Rational
ValueError: cannot coerce irrational Algebraic Real 1.414213562373095? to Rational
sage: v1 = QQbar(1/3 + I*sqrt(5))^7
sage: v2 = QQbar((100336/729*golden_ratio - 50168/729)*I)
sage: v = v1 + v2; v
Expand Down Expand Up @@ -5654,21 +5657,21 @@
sage: AA(golden_ratio)._integer_() # needs sage.symbolic
Traceback (most recent call last):
...
ValueError: Cannot coerce non-integral Algebraic Real 1.618033988749895? to Integer
ValueError: cannot coerce non-integral Algebraic Real 1.618033988749895? to Integer
sage: (AA(golden_ratio)^10 + AA(1-golden_ratio)^10)._integer_() # needs sage.symbolic
123
sage: AA(-22/7)._integer_()
Traceback (most recent call last):
...
ValueError: Cannot coerce non-integral Algebraic Real -22/7 to Integer
ValueError: cannot coerce non-integral Algebraic Real -22/7 to Integer
"""
if self._value.lower().ceiling() > self._value.upper().floor():
# The value is known to be non-integral.
raise ValueError(lazy_string("Cannot coerce non-integral Algebraic Real %s to Integer", self))
raise ValueError(lazy_string("cannot coerce non-integral Algebraic Real %s to Integer", self))

self.exactify()
if not isinstance(self._descr, ANRational):
raise ValueError(lazy_string("Cannot coerce irrational Algebraic Real %s to Integer", self))
raise ValueError(lazy_string("cannot coerce irrational Algebraic Real %s to Integer", self))

Check warning on line 5674 in src/sage/rings/qqbar.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/qqbar.py#L5674

Added line #L5674 was not covered by tests

return ZZ(self._descr._value)

Expand Down Expand Up @@ -5790,15 +5793,15 @@
sage: AA(sqrt(7))._rational_() # needs sage.symbolic
Traceback (most recent call last):
...
ValueError: Cannot coerce irrational Algebraic Real 2.645751311064591? to Rational
ValueError: cannot coerce irrational Algebraic Real 2.645751311064591? to Rational
sage: v = AA(1/2 + sqrt(2))^3 - AA(11/4*sqrt(2)); v # needs sage.symbolic
3.125000000000000?
sage: v._rational_() # needs sage.symbolic
25/8
"""
self.exactify()
if not isinstance(self._descr, ANRational):
raise ValueError(lazy_string("Cannot coerce irrational Algebraic Real %s to Rational", self))
raise ValueError(lazy_string("cannot coerce irrational Algebraic Real %s to Rational", self))

return QQ(self._descr._value)

Expand Down Expand Up @@ -6373,9 +6376,9 @@
TESTS::

sage: AA(-8)^(1/3)
-2
1.000000000000000? + 1.732050807568878?*I
sage: AA(-8)^(2/3)
4
-2.000000000000000? + 3.464101615137755?*I
sage: AA(32)^(3/5)
8
sage: AA(-16)^(1/2)
Expand All @@ -6398,7 +6401,7 @@
sage: act = AlgebraicNumberPowQQAction(QQ, AA); act
Right Rational Powering by Rational Field on Algebraic Real Field
sage: act(AA(-2), 1/3)
-1.259921049894873?
0.6299605249474365? + 1.091123635971722?*I

::

Expand Down Expand Up @@ -6429,19 +6432,16 @@

# Parent of the result
S = self.codomain()
if S is AA and d % 2 == 0 and x.sign() < 0:
if S is AA and x.sign() < 0:
S = QQbar

# First, check for exact roots.
# First, check for exact roots
if isinstance(x._descr, ANRational):
rt = rational_exact_root(abs(x._descr._value), d)
if rt is not None:
if x._descr._value < 0:
if S is AA:
return AlgebraicReal(ANRational((-rt)**n))
else:
z = QQbar.zeta(2 * d)._pow_int(n)
return z * AlgebraicNumber(ANRational(rt**n))
z = QQbar.zeta(2 * d)._pow_int(n)
return z * AlgebraicNumber(ANRational(rt**n))
return S(ANRational(rt**n))

if S is AA:
Expand Down
2 changes: 1 addition & 1 deletion src/sage/rings/universal_cyclotomic_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ def _algebraic_(self, R):
sage: AA(UCF.gen(5))
Traceback (most recent call last):
...
ValueError: Cannot coerce algebraic number with nonzero imaginary
ValueError: cannot coerce algebraic number with nonzero imaginary
part to algebraic real
"""
return R(QQbar(self))
Expand Down
40 changes: 40 additions & 0 deletions src/sage/structure/element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2976,6 +2976,46 @@ cdef class RingElement(ModuleElement):
return False
return self._parent.ideal(self).is_prime()

def pow(self, exponent):
"""
Return ``self`` raised to the power of ``exponent``.

INPUT:

- ``exponent`` -- integer or rational number

If ``exponent`` is a rational number, this method returns the
fractional power of ``self``, computed via :meth:`nth_root`.

Unlike powering by ``**``, the returned value is always
an element of the parent of ``self``.

EXAMPLES:

sage: ZZ(-8).pow(2/3)
4
sage: QQ(-8).pow(2/3)
4
sage: RR(-8).pow(2/3)
4.00000000000000
sage: CC(-8).pow(2/3)
-2.00000000000000 + 3.46410161513776*I

sage: # needs sage.rings.number_field
sage: AA(-8).pow(2/3)
4
sage: QQbar(-8).pow(2/3)
-2.000000000000000? + 3.464101615137755?*I
"""
if isinstance(exponent, int):
return self**exponent
try:
n = exponent.numerator()
d = exponent.denominator()
except AttributeError:
raise ArithmeticError("exponent must be an integer or a rational number")
return self.nth_root(d)**n


def is_CommutativeRingElement(x):
"""
Expand Down
36 changes: 18 additions & 18 deletions src/sage/symbolic/expression_conversion_algebraic.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@
from sage.symbolic.expression_conversions import Converter
from sage.symbolic.operators import add_vararg, mul_vararg
from sage.symbolic.ring import SR
from sage.rings.abc import AlgebraicRealField


#############
# Algebraic #
#############
class AlgebraicConverter(Converter):
def __init__(self, field):
"""
Expand All @@ -44,6 +42,11 @@ def __init__(self, field):
tan
"""
self.field = field
# converter for operands
if isinstance(field, AlgebraicRealField):
self.converter = AlgebraicConverter(field.algebraic_closure())
else:
self.converter = self

from sage.functions.all import reciprocal_trig_functions
self.reciprocal_trig_functions = reciprocal_trig_functions
Expand Down Expand Up @@ -97,6 +100,12 @@ def arithmetic(self, ex, operator):
sage: L = QuadraticField(3, embedding=-AA(3).sqrt())
sage: bool(L.gen() == -sqrt(3))
True

Test that :issue:12745 is fixed:

sage: x = exp(2*I*pi/7) + exp(-2*I*pi/7)
sage: AA(x)
1.246979603717467?
"""
# We try to avoid simplifying, because maxima's simplify command
# can change the value of a radical expression (by changing which
Expand All @@ -105,26 +114,17 @@ def arithmetic(self, ex, operator):
if operator is pow:
from sage.rings.rational import Rational
base, expt = ex.operands()
base = self.field(base)
base = self.converter.field(base)
expt = Rational(expt)
return self.field(base**expt)
else:
if operator is add_vararg:
operator = add
elif operator is mul_vararg:
operator = mul
return reduce(operator, map(self, ex.operands()))
if operator is add_vararg:
operator = add
elif operator is mul_vararg:
operator = mul
return self.field(reduce(operator, map(self.converter, ex.operands())))
except TypeError:
pass

if operator is pow:
from sage.symbolic.constants import e, pi, I
from sage.rings.rational_field import QQ

base, expt = ex.operands()
if base == e and expt / (pi * I) in QQ:
return exp(expt)._algebraic_(self.field)

raise TypeError("unable to convert %r to %s" % (ex, self.field))

def composition(self, ex, operator):
Expand Down
Loading