Lazy real and complex numbers¶
These classes are very lazy, in the sense that it doesn’t really do anything but simply sits between exact rings of characteristic 0 and the real numbers. The values are actually computed when they are cast into a field of fixed precision.
The main purpose of these classes is to provide a place for exact rings (e.g. number fields) to embed for the coercion model (as only one embedding can be specified in the forward direction).
- sage.rings.real_lazy.ComplexLazyField()[source]¶
Return the lazy complex field.
EXAMPLES:
There is only one lazy complex field:
sage: ComplexLazyField() is ComplexLazyField() True
>>> from sage.all import * >>> ComplexLazyField() is ComplexLazyField() True
ComplexLazyField() is ComplexLazyField()
- class sage.rings.real_lazy.ComplexLazyField_class[source]¶
Bases:
LazyField
This class represents the set of complex numbers to unspecified precision. For the most part it simply wraps exact elements and defers evaluation until a specified precision is requested.
For more information, see the documentation of the
RLF
.EXAMPLES:
sage: a = CLF(-1).sqrt() sage: a 1*I sage: CDF(a) 1.0*I sage: ComplexField(200)(a) 1.0000000000000000000000000000000000000000000000000000000000*I
>>> from sage.all import * >>> a = CLF(-Integer(1)).sqrt() >>> a 1*I >>> CDF(a) 1.0*I >>> ComplexField(Integer(200))(a) 1.0000000000000000000000000000000000000000000000000000000000*I
a = CLF(-1).sqrt() a CDF(a) ComplexField(200)(a)
- construction()[source]¶
Return the functorial construction of
self
, namely, algebraic closure of the real lazy field.EXAMPLES:
sage: c, S = CLF.construction(); S Real Lazy Field sage: CLF == c(S) True
>>> from sage.all import * >>> c, S = CLF.construction(); S Real Lazy Field >>> CLF == c(S) True
c, S = CLF.construction(); S CLF == c(S)
- gen(i=0)[source]¶
Return the \(i\)-th generator of
self
.EXAMPLES:
sage: CLF.gen() 1*I sage: ComplexField(100)(CLF.gen()) # needs sage.rings.number_field 1.0000000000000000000000000000*I
>>> from sage.all import * >>> CLF.gen() 1*I >>> ComplexField(Integer(100))(CLF.gen()) # needs sage.rings.number_field 1.0000000000000000000000000000*I
CLF.gen() ComplexField(100)(CLF.gen()) # needs sage.rings.number_field
- interval_field(prec=None)[source]¶
Return the interval field that represents the same mathematical field as
self
.EXAMPLES:
sage: CLF.interval_field() Complex Interval Field with 53 bits of precision sage: CLF.interval_field(333) # needs sage.rings.complex_interval_field Complex Interval Field with 333 bits of precision sage: CLF.interval_field() is CIF True
>>> from sage.all import * >>> CLF.interval_field() Complex Interval Field with 53 bits of precision >>> CLF.interval_field(Integer(333)) # needs sage.rings.complex_interval_field Complex Interval Field with 333 bits of precision >>> CLF.interval_field() is CIF True
CLF.interval_field() CLF.interval_field(333) # needs sage.rings.complex_interval_field CLF.interval_field() is CIF
- class sage.rings.real_lazy.LazyAlgebraic[source]¶
Bases:
LazyFieldElement
This represents an algebraic number, specified by a polynomial over \(\QQ\) and a real or complex approximation.
EXAMPLES:
sage: x = polygen(QQ) sage: from sage.rings.real_lazy import LazyAlgebraic sage: a = LazyAlgebraic(RLF, x^2-2, 1.5) sage: a 1.414213562373095?
>>> from sage.all import * >>> x = polygen(QQ) >>> from sage.rings.real_lazy import LazyAlgebraic >>> a = LazyAlgebraic(RLF, x**Integer(2)-Integer(2), RealNumber('1.5')) >>> a 1.414213562373095?
x = polygen(QQ) from sage.rings.real_lazy import LazyAlgebraic a = LazyAlgebraic(RLF, x^2-2, 1.5) a
- eval(R)[source]¶
Convert
self
into an element ofR
.EXAMPLES:
sage: from sage.rings.real_lazy import LazyAlgebraic sage: a = LazyAlgebraic(CLF, QQ['x'].cyclotomic_polynomial(7), 0.6+0.8*CC.0) sage: a 0.6234898018587335? + 0.7818314824680299?*I sage: ComplexField(150)(a) # indirect doctest # needs sage.rings.number_field 0.62348980185873353052500488400423981063227473 + 0.78183148246802980870844452667405775023233452*I sage: a = LazyAlgebraic(CLF, QQ['x'].0^2-7, -2.0) sage: RR(a) # needs sage.rings.number_field -2.64575131106459 sage: RR(a)^2 # needs sage.rings.number_field 7.00000000000000
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyAlgebraic >>> a = LazyAlgebraic(CLF, QQ['x'].cyclotomic_polynomial(Integer(7)), RealNumber('0.6')+RealNumber('0.8')*CC.gen(0)) >>> a 0.6234898018587335? + 0.7818314824680299?*I >>> ComplexField(Integer(150))(a) # indirect doctest # needs sage.rings.number_field 0.62348980185873353052500488400423981063227473 + 0.78183148246802980870844452667405775023233452*I >>> a = LazyAlgebraic(CLF, QQ['x'].gen(0)**Integer(2)-Integer(7), -RealNumber('2.0')) >>> RR(a) # needs sage.rings.number_field -2.64575131106459 >>> RR(a)**Integer(2) # needs sage.rings.number_field 7.00000000000000
from sage.rings.real_lazy import LazyAlgebraic a = LazyAlgebraic(CLF, QQ['x'].cyclotomic_polynomial(7), 0.6+0.8*CC.0) a ComplexField(150)(a) # indirect doctest # needs sage.rings.number_field a = LazyAlgebraic(CLF, QQ['x'].0^2-7, -2.0) RR(a) # needs sage.rings.number_field RR(a)^2 # needs sage.rings.number_field
- class sage.rings.real_lazy.LazyBinop[source]¶
Bases:
LazyFieldElement
A lazy element representing a binary (usually arithmetic) operation between two other lazy elements.
EXAMPLES:
sage: from sage.rings.real_lazy import LazyBinop sage: a = LazyBinop(RLF, 2, 1/3, operator.add) sage: a 2.333333333333334? sage: Reals(200)(a) 2.3333333333333333333333333333333333333333333333333333333333
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyBinop >>> a = LazyBinop(RLF, Integer(2), Integer(1)/Integer(3), operator.add) >>> a 2.333333333333334? >>> Reals(Integer(200))(a) 2.3333333333333333333333333333333333333333333333333333333333
from sage.rings.real_lazy import LazyBinop a = LazyBinop(RLF, 2, 1/3, operator.add) a Reals(200)(a)
- depth()[source]¶
Return the depth of
self
as an arithmetic expression.This is the maximum number of dependent intermediate expressions when evaluating
self
, and is used to determine the precision needed to get the final result to the desired number of bits.It is equal to the maximum of the right and left depths, plus one.
EXAMPLES:
sage: from sage.rings.real_lazy import LazyBinop sage: a = LazyBinop(RLF, 6, 8, operator.mul) sage: a.depth() 1 sage: b = LazyBinop(RLF, 2, a, operator.sub) sage: b.depth() 2
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyBinop >>> a = LazyBinop(RLF, Integer(6), Integer(8), operator.mul) >>> a.depth() 1 >>> b = LazyBinop(RLF, Integer(2), a, operator.sub) >>> b.depth() 2
from sage.rings.real_lazy import LazyBinop a = LazyBinop(RLF, 6, 8, operator.mul) a.depth() b = LazyBinop(RLF, 2, a, operator.sub) b.depth()
- eval(R)[source]¶
Convert the operands to elements of
R
, then perform the operation on them.EXAMPLES:
sage: from sage.rings.real_lazy import LazyBinop sage: a = LazyBinop(RLF, 6, 8, operator.add) sage: a.eval(RR) 14.0000000000000
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyBinop >>> a = LazyBinop(RLF, Integer(6), Integer(8), operator.add) >>> a.eval(RR) 14.0000000000000
from sage.rings.real_lazy import LazyBinop a = LazyBinop(RLF, 6, 8, operator.add) a.eval(RR)
A bit absurd:
sage: a.eval(str) '68'
>>> from sage.all import * >>> a.eval(str) '68'
a.eval(str)
- class sage.rings.real_lazy.LazyConstant[source]¶
Bases:
LazyFieldElement
This class represents a real or complex constant (such as
pi
orI
).- eval(R)[source]¶
Convert
self
into an element ofR
.EXAMPLES:
sage: from sage.rings.real_lazy import LazyConstant sage: a = LazyConstant(RLF, 'e') sage: RDF(a) # indirect doctest 2.718281828459045 sage: a = LazyConstant(CLF, 'I') sage: CC(a) 1.00000000000000*I
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyConstant >>> a = LazyConstant(RLF, 'e') >>> RDF(a) # indirect doctest 2.718281828459045 >>> a = LazyConstant(CLF, 'I') >>> CC(a) 1.00000000000000*I
from sage.rings.real_lazy import LazyConstant a = LazyConstant(RLF, 'e') RDF(a) # indirect doctest a = LazyConstant(CLF, 'I') CC(a)
- class sage.rings.real_lazy.LazyField[source]¶
Bases:
Field
The base class for lazy real fields.
Warning
LazyField uses
__getattr__()
, to implement:sage: CLF.pi 3.141592653589794?
>>> from sage.all import * >>> CLF.pi 3.141592653589794?
CLF.pi
I (NT, 20/04/2012) did not manage to have
__getattr__
callParent.__getattr__()
in case of failure; hence we can’t use this__getattr__
trick for extension types to recover the methods from categories. Therefore, at this point, no concrete subclass of this class should be an extension type (which is probably just fine):sage: RLF.__class__ <class 'sage.rings.real_lazy.RealLazyField_class_with_category'> sage: CLF.__class__ <class 'sage.rings.real_lazy.ComplexLazyField_class_with_category'>
>>> from sage.all import * >>> RLF.__class__ <class 'sage.rings.real_lazy.RealLazyField_class_with_category'> >>> CLF.__class__ <class 'sage.rings.real_lazy.ComplexLazyField_class_with_category'>
RLF.__class__ CLF.__class__
- Element[source]¶
alias of
LazyWrapper
- algebraic_closure()[source]¶
Return the algebraic closure of
self
, i.e., the complex lazy field.EXAMPLES:
sage: RLF.algebraic_closure() Complex Lazy Field sage: CLF.algebraic_closure() Complex Lazy Field
>>> from sage.all import * >>> RLF.algebraic_closure() Complex Lazy Field >>> CLF.algebraic_closure() Complex Lazy Field
RLF.algebraic_closure() CLF.algebraic_closure()
- class sage.rings.real_lazy.LazyFieldElement[source]¶
Bases:
FieldElement
- approx()[source]¶
Return
self
as an element of an interval field.EXAMPLES:
sage: CLF(1/6).approx() 0.1666666666666667? sage: CLF(1/6).approx().parent() Complex Interval Field with 53 bits of precision
>>> from sage.all import * >>> CLF(Integer(1)/Integer(6)).approx() 0.1666666666666667? >>> CLF(Integer(1)/Integer(6)).approx().parent() Complex Interval Field with 53 bits of precision
CLF(1/6).approx() CLF(1/6).approx().parent()
When the absolute value is involved, the result might be real:
sage: # needs sage.symbolic sage: z = exp(CLF(1 + I/2)); z 2.38551673095914? + 1.303213729686996?*I sage: r = z.abs(); r 2.71828182845905? sage: parent(z.approx()) Complex Interval Field with 53 bits of precision sage: parent(r.approx()) Real Interval Field with 53 bits of precision
>>> from sage.all import * >>> # needs sage.symbolic >>> z = exp(CLF(Integer(1) + I/Integer(2))); z 2.38551673095914? + 1.303213729686996?*I >>> r = z.abs(); r 2.71828182845905? >>> parent(z.approx()) Complex Interval Field with 53 bits of precision >>> parent(r.approx()) Real Interval Field with 53 bits of precision
# needs sage.symbolic z = exp(CLF(1 + I/2)); z r = z.abs(); r parent(z.approx()) parent(r.approx())
- continued_fraction()[source]¶
Return the continued fraction of
self
.EXAMPLES:
sage: # needs sage.symbolic sage: a = RLF(sqrt(2)) + RLF(sqrt(3)) sage: cf = a.continued_fraction() sage: cf [3; 6, 1, 5, 7, 1, 1, 4, 1, 38, 43, 1, 3, 2, 1, 1, 1, 1, 2, 4, ...] sage: cf.convergent(100) 444927297812646558239761867973501208151173610180916865469/141414466649174973335183571854340329919207428365474086063
>>> from sage.all import * >>> # needs sage.symbolic >>> a = RLF(sqrt(Integer(2))) + RLF(sqrt(Integer(3))) >>> cf = a.continued_fraction() >>> cf [3; 6, 1, 5, 7, 1, 1, 4, 1, 38, 43, 1, 3, 2, 1, 1, 1, 1, 2, 4, ...] >>> cf.convergent(Integer(100)) 444927297812646558239761867973501208151173610180916865469/141414466649174973335183571854340329919207428365474086063
# needs sage.symbolic a = RLF(sqrt(2)) + RLF(sqrt(3)) cf = a.continued_fraction() cf cf.convergent(100)
- depth()[source]¶
Abstract method for returning the depth of
self
as an arithmetic expression.This is the maximum number of dependent intermediate expressions when evaluating
self
, and is used to determine the precision needed to get the final result to the desired number of bits.It is equal to the maximum of the right and left depths, plus one.
EXAMPLES:
sage: from sage.rings.real_lazy import LazyBinop sage: a = LazyBinop(RLF, 6, 8, operator.mul) sage: a.depth() 1
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyBinop >>> a = LazyBinop(RLF, Integer(6), Integer(8), operator.mul) >>> a.depth() 1
from sage.rings.real_lazy import LazyBinop a = LazyBinop(RLF, 6, 8, operator.mul) a.depth()
- class sage.rings.real_lazy.LazyNamedUnop[source]¶
Bases:
LazyUnop
This class is used to represent the many named methods attached to real numbers, and is instantiated by the
__getattr__
method ofLazyElements
.EXAMPLES:
sage: from sage.rings.real_lazy import LazyNamedUnop sage: a = LazyNamedUnop(RLF, 1, 'arcsin') sage: RR(a) 1.57079632679490 sage: a = LazyNamedUnop(RLF, 9, 'log', extra_args=(3,)) sage: RR(a) 2.00000000000000
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyNamedUnop >>> a = LazyNamedUnop(RLF, Integer(1), 'arcsin') >>> RR(a) 1.57079632679490 >>> a = LazyNamedUnop(RLF, Integer(9), 'log', extra_args=(Integer(3),)) >>> RR(a) 2.00000000000000
from sage.rings.real_lazy import LazyNamedUnop a = LazyNamedUnop(RLF, 1, 'arcsin') RR(a) a = LazyNamedUnop(RLF, 9, 'log', extra_args=(3,)) RR(a)
- class sage.rings.real_lazy.LazyUnop[source]¶
Bases:
LazyFieldElement
Represent a unevaluated single function of one variable.
EXAMPLES:
sage: from sage.rings.real_lazy import LazyUnop sage: a = LazyUnop(RLF, 3, sqrt); a 1.732050807568878? sage: a._arg 3 sage: a._op <function sqrt at ...> sage: Reals(100)(a) 1.7320508075688772935274463415 sage: Reals(100)(a)^2 3.0000000000000000000000000000
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyUnop >>> a = LazyUnop(RLF, Integer(3), sqrt); a 1.732050807568878? >>> a._arg 3 >>> a._op <function sqrt at ...> >>> Reals(Integer(100))(a) 1.7320508075688772935274463415 >>> Reals(Integer(100))(a)**Integer(2) 3.0000000000000000000000000000
from sage.rings.real_lazy import LazyUnop a = LazyUnop(RLF, 3, sqrt); a a._arg a._op Reals(100)(a) Reals(100)(a)^2
- depth()[source]¶
Return the depth of
self
as an arithmetic expression.This is the maximum number of dependent intermediate expressions when evaluating
self
, and is used to determine the precision needed to get the final result to the desired number of bits.It is equal to one more than the depth of its operand.
EXAMPLES:
sage: from sage.rings.real_lazy import LazyUnop sage: a = LazyUnop(RLF, 3, sqrt) sage: a.depth() 1 sage: b = LazyUnop(RLF, a, sin) sage: b.depth() 2
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyUnop >>> a = LazyUnop(RLF, Integer(3), sqrt) >>> a.depth() 1 >>> b = LazyUnop(RLF, a, sin) >>> b.depth() 2
from sage.rings.real_lazy import LazyUnop a = LazyUnop(RLF, 3, sqrt) a.depth() b = LazyUnop(RLF, a, sin) b.depth()
- eval(R)[source]¶
Convert
self
into an element ofR
.EXAMPLES:
sage: from sage.rings.real_lazy import LazyUnop sage: a = LazyUnop(RLF, 3, sqrt) sage: a.eval(ZZ) # needs sage.symbolic sqrt(3)
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyUnop >>> a = LazyUnop(RLF, Integer(3), sqrt) >>> a.eval(ZZ) # needs sage.symbolic sqrt(3)
from sage.rings.real_lazy import LazyUnop a = LazyUnop(RLF, 3, sqrt) a.eval(ZZ) # needs sage.symbolic
- class sage.rings.real_lazy.LazyWrapper[source]¶
Bases:
LazyFieldElement
A lazy element that simply wraps an element of another ring.
EXAMPLES:
sage: from sage.rings.real_lazy import LazyWrapper sage: a = LazyWrapper(RLF, 3) sage: a._value 3
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyWrapper >>> a = LazyWrapper(RLF, Integer(3)) >>> a._value 3
from sage.rings.real_lazy import LazyWrapper a = LazyWrapper(RLF, 3) a._value
- continued_fraction()[source]¶
Return the continued fraction of
self
.EXAMPLES:
sage: a = RLF(sqrt(2)) # needs sage.symbolic sage: a.continued_fraction() # needs sage.symbolic [1; 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...]
>>> from sage.all import * >>> a = RLF(sqrt(Integer(2))) # needs sage.symbolic >>> a.continued_fraction() # needs sage.symbolic [1; 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ...]
a = RLF(sqrt(2)) # needs sage.symbolic a.continued_fraction() # needs sage.symbolic
- class sage.rings.real_lazy.LazyWrapperMorphism[source]¶
Bases:
Morphism
This morphism coerces elements from anywhere into lazy rings by creating a wrapper element (as fast as possible).
EXAMPLES:
sage: from sage.rings.real_lazy import LazyWrapperMorphism sage: f = LazyWrapperMorphism(QQ, RLF) sage: a = f(3); a 3 sage: type(a) <class 'sage.rings.real_lazy.LazyWrapper'> sage: a._value 3 sage: a._value.parent() Rational Field
>>> from sage.all import * >>> from sage.rings.real_lazy import LazyWrapperMorphism >>> f = LazyWrapperMorphism(QQ, RLF) >>> a = f(Integer(3)); a 3 >>> type(a) <class 'sage.rings.real_lazy.LazyWrapper'> >>> a._value 3 >>> a._value.parent() Rational Field
from sage.rings.real_lazy import LazyWrapperMorphism f = LazyWrapperMorphism(QQ, RLF) a = f(3); a type(a) a._value a._value.parent()
- sage.rings.real_lazy.RealLazyField()[source]¶
Return the lazy real field.
EXAMPLES:
There is only one lazy real field:
sage: RealLazyField() is RealLazyField() True
>>> from sage.all import * >>> RealLazyField() is RealLazyField() True
RealLazyField() is RealLazyField()
- class sage.rings.real_lazy.RealLazyField_class[source]¶
Bases:
LazyField
This class represents the set of real numbers to unspecified precision. For the most part it simply wraps exact elements and defers evaluation until a specified precision is requested.
Its primary use is to connect the exact rings (such as number fields) to fixed precision real numbers. For example, to specify an embedding of a number field \(K\) into \(\RR\) one can map into this field and the coercion will then be able to carry the mapping to real fields of any precision.
EXAMPLES:
sage: a = RLF(1/3) sage: a 0.3333333333333334? sage: a + 1/5 0.5333333333333334? sage: a = RLF(1/3) sage: a 0.3333333333333334? sage: a + 5 5.333333333333334? sage: RealField(100)(a+5) 5.3333333333333333333333333333
>>> from sage.all import * >>> a = RLF(Integer(1)/Integer(3)) >>> a 0.3333333333333334? >>> a + Integer(1)/Integer(5) 0.5333333333333334? >>> a = RLF(Integer(1)/Integer(3)) >>> a 0.3333333333333334? >>> a + Integer(5) 5.333333333333334? >>> RealField(Integer(100))(a+Integer(5)) 5.3333333333333333333333333333
a = RLF(1/3) a a + 1/5 a = RLF(1/3) a a + 5 RealField(100)(a+5)
sage: CC.0 + RLF(1/3) 0.333333333333333 + 1.00000000000000*I sage: ComplexField(200).0 + RLF(1/3) 0.33333333333333333333333333333333333333333333333333333333333 + 1.0000000000000000000000000000000000000000000000000000000000*I
>>> from sage.all import * >>> CC.gen(0) + RLF(Integer(1)/Integer(3)) 0.333333333333333 + 1.00000000000000*I >>> ComplexField(Integer(200)).gen(0) + RLF(Integer(1)/Integer(3)) 0.33333333333333333333333333333333333333333333333333333333333 + 1.0000000000000000000000000000000000000000000000000000000000*I
CC.0 + RLF(1/3) ComplexField(200).0 + RLF(1/3)
>>> from sage.all import * >>> CC.gen(0) + RLF(Integer(1)/Integer(3)) 0.333333333333333 + 1.00000000000000*I >>> ComplexField(Integer(200)).gen(0) + RLF(Integer(1)/Integer(3)) 0.33333333333333333333333333333333333333333333333333333333333 + 1.0000000000000000000000000000000000000000000000000000000000*I
CC.0 + RLF(1/3) ComplexField(200).0 + RLF(1/3)
- construction()[source]¶
Return the functorial construction of
self
, namely, the completion of the rationals at infinity to infinite precision.EXAMPLES:
sage: c, S = RLF.construction(); S Rational Field sage: RLF == c(S) True
>>> from sage.all import * >>> c, S = RLF.construction(); S Rational Field >>> RLF == c(S) True
c, S = RLF.construction(); S RLF == c(S)
- gen(i=0)[source]¶
Return the \(i\)-th generator of
self
.EXAMPLES:
sage: RLF.gen() 1
>>> from sage.all import * >>> RLF.gen() 1
RLF.gen()
- interval_field(prec=None)[source]¶
Return the interval field that represents the same mathematical field as
self
.EXAMPLES:
sage: RLF.interval_field() Real Interval Field with 53 bits of precision sage: RLF.interval_field(200) Real Interval Field with 200 bits of precision
>>> from sage.all import * >>> RLF.interval_field() Real Interval Field with 53 bits of precision >>> RLF.interval_field(Integer(200)) Real Interval Field with 200 bits of precision
RLF.interval_field() RLF.interval_field(200)
- sage.rings.real_lazy.make_element(parent, *args)[source]¶
Create an element of
parent
.EXAMPLES:
sage: a = RLF(pi) + RLF(sqrt(1/2)) # indirect doctest # needs sage.symbolic sage: bool(loads(dumps(a)) == a) # needs sage.symbolic True
>>> from sage.all import * >>> a = RLF(pi) + RLF(sqrt(Integer(1)/Integer(2))) # indirect doctest # needs sage.symbolic >>> bool(loads(dumps(a)) == a) # needs sage.symbolic True
a = RLF(pi) + RLF(sqrt(1/2)) # indirect doctest # needs sage.symbolic bool(loads(dumps(a)) == a) # needs sage.symbolic