Skip to content

Commit

Permalink
Trac #18036: I.parent() should not be the symbolic ring
Browse files Browse the repository at this point in the history
As suggested in #7545, this ticket defines the imaginary unit `I`
directly as the generator of `QuadraticField(-1)` instead of wrapping it
in a symbolic expression.

**Why?** To allow it to be used in combination with elements of QQbar,
CC, etc., without coercion forcing the expression to SR. For example,
`1.0 + I` is now an element of CC instead of SR.

**How?** We set `I` in sage.all to the generator of ℚ[i], and deprecate
importing it from `sage.symbolic.I`. The symbolic `I` remains available
from `sage.symbolic.constants` for library code working with symbolic
expressions, and as `SR(I)` or `SR.I()`. We create a dedicated subclass
of quadratic number field elements to make it possible to support
features similar to those of symbolic expressions of the form `a + I*b`
that would not make sense for number field elements (or be too hard to
implement, or pollute the namespace).

**Why not ℤ[i]?** Because the class hierarchy of number field and order
elements makes it difficult to provide the compatibility features
mentioned above for elements of both ℤ[i] and ℚ[i]. Having `I` be an
element of ℚ[i] covers almost all use cases (all except working with
algebraic integers?), and people who work with orders are sophisticated
enough to explicitly ask for I ∈ ℤ[i] when they need that. (This is a
debatable choice. We could probably do without the dedicated subclass
for elements of ℚ[i], at the price of breaking backward compatibility a
bit more.)

URL: https://trac.sagemath.org/18036
Reported by: vdelecroix
Ticket author(s): Marc Mezzarobba
Reviewer(s): Vincent Delecroix
  • Loading branch information
Release Manager committed Nov 22, 2020
2 parents 92e54d9 + 54a34a7 commit 027ca17
Show file tree
Hide file tree
Showing 46 changed files with 305 additions and 165 deletions.
4 changes: 2 additions & 2 deletions src/doc/en/prep/Programming.rst
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ not have :math:`I=\sqrt{-1}`, decimal points, or division.
sage: parent(c)
Real Field with 53 bits of precision
sage: parent(d)
Symbolic Ring
Number Field in I with defining polynomial x^2 + 1 with I = 1*I
sage: parent(e)
Symbolic Ring
Complex Field with 53 bits of precision

3 changes: 2 additions & 1 deletion src/sage/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@
from sage.rings.qqbar import _init_qqbar
_init_qqbar()


###########################################################
#### WARNING:
# DO *not* import numpy / matplotlib / networkx here!!
Expand All @@ -241,6 +240,8 @@
true = True
false = False
oo = infinity
from sage.rings.imaginary_unit import I
i = I

from sage.misc.copying import license
copying = license
Expand Down
4 changes: 1 addition & 3 deletions src/sage/categories/rings.py
Original file line number Diff line number Diff line change
Expand Up @@ -999,7 +999,7 @@ def __getitem__(self, arg):
and orders in number fields::
sage: ZZ[I]
Order in Number Field in I with defining polynomial x^2 + 1 with I = 1*I
Order in Number Field in I0 with defining polynomial x^2 + 1 with I0 = 1*I
sage: ZZ[sqrt(5)]
Order in Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?
sage: ZZ[sqrt(2)+sqrt(3)]
Expand Down Expand Up @@ -1054,8 +1054,6 @@ def __getitem__(self, arg):
Embeddings::
sage: QQ[I](I.pyobject())
I
sage: a = 10^100; expr = (2*a + sqrt(2))/(2*a^2-1)
sage: QQ[expr].coerce_embedding() is None
False
Expand Down
2 changes: 1 addition & 1 deletion src/sage/functions/special.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@
from sage.symbolic.function import BuiltinFunction
from sage.libs.mpmath import utils as mpmath_utils
from sage.functions.all import sqrt, sin, cot, exp
from sage.symbolic.all import I
from sage.symbolic.constants import I


class SphericalHarmonic(BuiltinFunction):
Expand Down
2 changes: 1 addition & 1 deletion src/sage/geometry/hyperbolic_space/hyperbolic_coercion.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
#***********************************************************************

from sage.categories.morphism import Morphism
from sage.symbolic.all import I
from sage.symbolic.constants import I
from sage.matrix.constructor import matrix
from sage.modules.free_module_element import vector
from sage.rings.integer import Integer
Expand Down
2 changes: 1 addition & 1 deletion src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
# **********************************************************************

from sage.structure.sage_object import SageObject
from sage.symbolic.all import I
from sage.symbolic.constants import I
from sage.misc.lazy_attribute import lazy_attribute
from sage.rings.infinity import infinity
from sage.rings.all import CC, RR
Expand Down
2 changes: 1 addition & 1 deletion src/sage/geometry/hyperbolic_space/hyperbolic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
from sage.functions.all import arccosh
from sage.rings.all import CC, RR, RDF
from sage.rings.infinity import infinity
from sage.symbolic.all import I
from sage.symbolic.constants import I
from sage.matrix.constructor import matrix
from sage.categories.homset import Hom

Expand Down
2 changes: 1 addition & 1 deletion src/sage/geometry/hyperbolic_space/hyperbolic_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@

from sage.structure.element import Element
from sage.structure.richcmp import richcmp, op_NE
from sage.symbolic.all import I
from sage.symbolic.constants import I
from sage.misc.latex import latex
from sage.structure.element import is_Matrix
from sage.matrix.constructor import matrix
Expand Down
8 changes: 4 additions & 4 deletions src/sage/groups/abelian_gps/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class AbelianGroupWithValuesEmbedding(Morphism):
sage: embedding = Z4.values_embedding(); embedding
Generic morphism:
From: Multiplicative Abelian group isomorphic to C4
To: Symbolic Ring
To: Number Field in I with defining polynomial x^2 + 1 with I = 1*I
sage: embedding(1)
1
sage: embedding(g)
Expand All @@ -184,7 +184,7 @@ def __init__(self, domain, codomain):
sage: AbelianGroupWithValuesEmbedding(Z4, Z4.values_group())
Generic morphism:
From: Multiplicative Abelian group isomorphic to C4
To: Symbolic Ring
To: Number Field in I with defining polynomial x^2 + 1 with I = 1*I
"""
assert domain.values_group() is codomain
from sage.categories.homset import Hom
Expand Down Expand Up @@ -479,7 +479,7 @@ def values_group(self):
sage: Z4 = AbelianGroupWithValues([I], [4])
sage: Z4.values_group()
Symbolic Ring
Number Field in I with defining polynomial x^2 + 1 with I = 1*I
"""
return self._values_group

Expand All @@ -497,6 +497,6 @@ def values_embedding(self):
sage: Z4.values_embedding()
Generic morphism:
From: Multiplicative Abelian group isomorphic to C4
To: Symbolic Ring
To: Number Field in I with defining polynomial x^2 + 1 with I = 1*I
"""
return AbelianGroupWithValuesEmbedding(self, self.values_group())
6 changes: 2 additions & 4 deletions src/sage/interfaces/fricas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1563,8 +1563,7 @@ def _sage_expression(fricas_InputForm):
D[0, 0, 1](f)(x + y, y)
"""
from sage.libs.pynac.pynac import register_symbol
from sage.symbolic.all import I
from sage.symbolic.constants import e, pi
from sage.symbolic.constants import e, pi, I
from sage.calculus.functional import diff
from sage.functions.log import dilog, lambert_w
from sage.functions.trig import sin, cos, tan, cot, sec, csc
Expand Down Expand Up @@ -1838,10 +1837,9 @@ def _sage_(self):
<BLANKLINE>
Cannot convert the value from type Any to InputForm .
"""
from sage.rings.all import PolynomialRing, RDF
from sage.rings.all import PolynomialRing, RDF, I
from sage.rings.real_mpfr import RealField
from sage.symbolic.ring import SR
from sage.symbolic.all import I
from sage.matrix.constructor import matrix
from sage.modules.free_module_element import vector
from sage.structure.factorization import Factorization
Expand Down
6 changes: 3 additions & 3 deletions src/sage/interfaces/gp.py
Original file line number Diff line number Diff line change
Expand Up @@ -891,9 +891,9 @@ def _sage_(self):
EXAMPLES::
sage: gp(I).sage()
sage: gp(SR(I)).sage()
i
sage: gp(I).sage().parent()
sage: gp(SR(I)).sage().parent()
Number Field in i with defining polynomial x^2 + 1 with i = 1*I
::
Expand Down Expand Up @@ -971,7 +971,7 @@ def _complex_mpfr_field_(self, CC):
EXAMPLES::
sage: z = gp(1+15*I); z
sage: z = gp(SR(1+15*I)); z
1 + 15*I
sage: z._complex_mpfr_field_(CC)
1.00000000000000 + 15.0000000000000*I
Expand Down
22 changes: 13 additions & 9 deletions src/sage/libs/pari/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,14 @@
0
sage: pari(-1/2).sign()
-1
sage: pari(I).sign()
sage: pari(SR(I)).sign()
Traceback (most recent call last):
...
PariError: incorrect type in gsigne (t_COMPLEX)
sage: pari(I).sign()
Traceback (most recent call last):
...
PariError: incorrect type in gsigne (t_POLMOD)
sage: y = pari('y')
sage: x = pari('9') + y - y
Expand Down Expand Up @@ -1197,7 +1201,7 @@
[0, 1/2, 0, -3/4, 0, 2, -3/2, 0, -9/16, 40, -116, 117/4, 256000/117, Vecsmall([1]), [Vecsmall([64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]]
sage: pari([0,0.5,0,-0.75,0]).ellinit()
[0, 0.500000000000000, 0, -0.750000000000000, 0, 2.00000000000000, -1.50000000000000, 0, -0.562500000000000, 40.0000000000000, -116.000000000000, 29.2500000000000, 2188.03418803419, Vecsmall([0]), [Vecsmall([64, 1])], [0, 0, 0, 0]]
sage: pari([0,I,0,1,0]).ellinit()
sage: pari([0,SR(I),0,1,0]).ellinit()
[0, I, 0, 1, 0, 4*I, 2, 0, -1, -64, 352*I, -80, 16384/5, Vecsmall([0]), [Vecsmall([64, 0])], [0, 0, 0, 0]]
sage: x = SR.symbol('x')
sage: pari([0,x,0,2*x,1]).ellinit()
Expand Down Expand Up @@ -1336,9 +1340,9 @@
sage: e = pari([0,1,1,-2,0]).ellinit()
sage: e.ellordinate(0)
[0, -1]
sage: e.ellordinate(I)
sage: e.ellordinate(SR(I))
[0.582203589721741 - 1.38606082464177*I, -1.58220358972174 + 1.38606082464177*I]
sage: e.ellordinate(I, precision=128)[0].sage()
sage: e.ellordinate(SR(I), precision=128)[0].sage()
0.58220358972174117723338947874993600727 - 1.3860608246417697185311834209833653345*I
sage: e.ellordinate(1+3*5^1+O(5^3))
[4*5 + 5^2 + O(5^3), 4 + 3*5^2 + O(5^3)]
Expand All @@ -1361,9 +1365,9 @@
[0]
sage: e.ellmul(p, 2)
[1/4, -7/8]
sage: q = e.ellmul(p, 1+I); q
sage: q = e.ellmul(p, SR(1+I)); q
[-2*I, 1 + I]
sage: e.ellmul(q, 1-I)
sage: e.ellmul(q, SR(1-I))
[1/4, -7/8]
sage: for D in [-7, -8, -11, -12, -16, -19, -27, -28]: # long time (1s)
....: hcpol = hilbert_class_polynomial(D)
Expand Down Expand Up @@ -1416,15 +1420,15 @@
sage: e.ellztopoint(0)
[0]
sage: pari(I).ellj()
sage: pari(SR(I)).ellj()
1728.00000000000
sage: pari(3*I).ellj()
sage: pari(SR(3*I)).ellj()
153553679.396729
sage: pari('quadgen(-3)').ellj()
0.E-54
sage: pari('quadgen(-7)').ellj(precision=256).sage()
-3375.000000000000000000000000000000000000000000000000000000000000000000000000
sage: pari(-I).ellj()
sage: pari(SR(-I)).ellj()
Traceback (most recent call last):
...
PariError: domain error in modular function: Im(argument) <= 0
Expand Down
56 changes: 28 additions & 28 deletions src/sage/libs/pynac/pynac.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1174,9 +1174,9 @@ def py_is_crational_for_doctest(x):
True
sage: py_is_crational_for_doctest(1.5)
False
sage: py_is_crational_for_doctest(I.pyobject())
sage: py_is_crational_for_doctest(I)
True
sage: py_is_crational_for_doctest(I.pyobject()+1/2)
sage: py_is_crational_for_doctest(I+1/2)
True
"""
return py_is_crational(x)
Expand Down Expand Up @@ -1310,11 +1310,11 @@ def py_is_cinteger_for_doctest(x):
sage: from sage.libs.pynac.pynac import py_is_cinteger_for_doctest
sage: py_is_cinteger_for_doctest(1)
True
sage: py_is_cinteger_for_doctest(I.pyobject())
sage: py_is_cinteger_for_doctest(I)
True
sage: py_is_cinteger_for_doctest(I.pyobject() - 3)
sage: py_is_cinteger_for_doctest(I - 3)
True
sage: py_is_cinteger_for_doctest(I.pyobject() + 1/2)
sage: py_is_cinteger_for_doctest(I + 1/2)
False
"""
return py_is_cinteger(x)
Expand Down Expand Up @@ -2350,20 +2350,21 @@ def init_pynac_I():
EXAMPLES::
sage: I
sage: from sage.libs.pynac.pynac import I as symbolic_I
sage: symbolic_I
I
sage: I^2
sage: symbolic_I^2
-1
Note that conversions to real fields will give TypeErrors::
sage: float(I)
sage: float(symbolic_I)
Traceback (most recent call last):
...
TypeError: unable to simplify to float approximation
sage: gp(I)
sage: gp(symbolic_I)
I
sage: RR(I)
sage: RR(symbolic_I)
Traceback (most recent call last):
...
TypeError: unable to convert '1.00000000000000*I' to a real number
Expand All @@ -2372,53 +2373,53 @@ def init_pynac_I():
sage: C = ComplexField(200); C
Complex Field with 200 bits of precision
sage: C(I)
sage: C(symbolic_I)
1.0000000000000000000000000000000000000000000000000000000000*I
sage: I._complex_mpfr_field_(ComplexField(53))
sage: symbolic_I._complex_mpfr_field_(ComplexField(53))
1.00000000000000*I
sage: I._complex_double_(CDF)
sage: symbolic_I._complex_double_(CDF)
1.0*I
sage: CDF(I)
sage: CDF(symbolic_I)
1.0*I
sage: z = I + I; z
sage: z = symbolic_I + symbolic_I; z
2*I
sage: C(z)
2.0000000000000000000000000000000000000000000000000000000000*I
sage: 1e8*I
sage: 1e8*symbolic_I
1.00000000000000e8*I
sage: complex(I)
sage: complex(symbolic_I)
1j
sage: QQbar(I)
sage: QQbar(symbolic_I)
I
sage: abs(I)
sage: abs(symbolic_I)
1
sage: I.minpoly()
sage: symbolic_I.minpoly()
x^2 + 1
sage: maxima(2*I)
sage: maxima(2*symbolic_I)
2*%i
TESTS:
sage: repr(I)
sage: repr(symbolic_I)
'I'
sage: latex(I)
sage: latex(symbolic_I)
i
sage: sage.libs.pynac.pynac.init_pynac_I()
sage: type(sage.libs.pynac.pynac.I)
<type 'sage.symbolic.expression.Expression'>
sage: type(sage.libs.pynac.pynac.I.pyobject())
<type 'sage.rings.number_field.number_field_element_quadratic.NumberFieldElement_quadratic'>
<type 'sage.rings.number_field.number_field_element_quadratic.NumberFieldElement_gaussian'>
Check that :trac:`10064` is fixed::
sage: y = I*I*x / x # so y is the expression -1
sage: y = symbolic_I*symbolic_I*x / x # so y is the expression -1
sage: y.is_positive()
False
sage: z = -x / x
Expand All @@ -2428,9 +2429,8 @@ def init_pynac_I():
True
"""
global pynac_I, I
from sage.rings.number_field.number_field import QuadraticField
K = QuadraticField(-1, 'I', embedding=CC.gen(), latex_name='i')
pynac_I = K.gen()
from sage.rings.number_field.number_field import GaussianField
pynac_I = GaussianField().gen()
ginac_pyinit_I(pynac_I)
I = new_Expression_from_GEx(ring.SR, g_I)

Expand Down
4 changes: 2 additions & 2 deletions src/sage/matrix/matrix0.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -2865,7 +2865,7 @@ cdef class Matrix(sage.structure.element.Matrix):
If not, we get an error message::
sage: a.add_multiple_of_row(1,0,i)
sage: a.add_multiple_of_row(1,0,SR.I())
Traceback (most recent call last):
...
TypeError: Multiplying row by Symbolic Ring element cannot be done over Rational Field, use change_ring or with_added_multiple_of_row instead.
Expand Down Expand Up @@ -2949,7 +2949,7 @@ cdef class Matrix(sage.structure.element.Matrix):
If not, we get an error message::
sage: a.add_multiple_of_column(1,0,i)
sage: a.add_multiple_of_column(1,0,SR.I())
Traceback (most recent call last):
...
TypeError: Multiplying column by Symbolic Ring element cannot be done over Rational Field, use change_ring or with_added_multiple_of_column instead.
Expand Down
2 changes: 1 addition & 1 deletion src/sage/matrix/special.py
Original file line number Diff line number Diff line change
Expand Up @@ -1233,7 +1233,7 @@ def elementary_matrix(arg0, arg1=None, **kwds):
sage: E = elementary_matrix(4, row1=1, scale=I)
sage: E.parent()
Full MatrixSpace of 4 by 4 dense matrices over Symbolic Ring
Full MatrixSpace of 4 by 4 dense matrices over Number Field in I with defining polynomial x^2 + 1 with I = 1*I
sage: E = elementary_matrix(4, row1=1, scale=CDF(I))
sage: E.parent()
Expand Down
Loading

0 comments on commit 027ca17

Please sign in to comment.