Navigation

Carl Witty (2007-10-29): massive rewrite to support complex as well as real numbers

This is an implementation of the algebraic numbers (the complex
numbers which are the zero of a polynomial in \(\ZZ[x]\); in other
words, the algebraic closure of \(\QQ\), with an embedding into \(\CC\)).
All computations are exact. We also include an implementation of the
algebraic reals (the intersection of the algebraic numbers with
\(\RR\)). The field of algebraic numbers \(\QQbar\) is available with
abbreviation QQbar; the field of algebraic reals has abbreviation
AA.

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).

a particular root of a polynomial, given as a polynomial with
algebraic coefficients together with an isolating interval (given as
a RealIntervalFieldElement) which encloses exactly one root, and
the multiplicity of the root

a polynomial in one generator, where the generator is an algebraic
number given as the root of an irreducible polynomial with integral
coefficients and the polynomial is given as a
NumberFieldElement.

An algebraic number can be coerced into ComplexIntervalField (or
RealIntervalField, for algebraic reals); every algebraic number has a
cached interval of the highest precision yet calculated.

In most cases, computations that need to compare two algebraic numbers
compute them with 128-bit precision intervals; if this does not suffice to
prove that the numbers are different, then we fall back on exact
computation.

Note that division involves an implicit comparison of the divisor against
zero, and may thus trigger exact computation.

Also, using an algebraic number in the leading coefficient of
a polynomial also involves an implicit comparison against zero, which
again may trigger exact computation.

Note that we work fairly hard to avoid computing new number fields;
to help, we keep a lattice of already-computed number fields and
their inclusions.

For a monic cubic polynomial \(x^3 + bx^2 + cx + d\) with roots \(s1\),
\(s2\), \(s3\), the discriminant is defined as
\((s1-s2)^2(s1-s3)^2(s2-s3)^2\) and can be computed as \(b^2c^2 - 4b^3d -
4c^3 + 18bcd - 27d^2\). We can test that these definitions do give the
same result:

We can explicitly coerce from \(\QQ[I]\). (Technically, this is not quite
kosher, since \(\QQ[I]\) does not come with an embedding; we do not know
whether the field generator is supposed to map to \(+I\) or \(-I\). We assume
that for any quadratic field with polynomial \(x^2+1\), the generator maps
to \(+I\).):

We can find the absolute value and norm of an algebraic number exactly.
(Note that we define the norm as the product of a number and its
complex conjugate; this is the algebraic definition of norm, if we
view QQbar as AA[I].):

Given an algebraic number, we can produce a string that will reproduce
that algebraic number if you type the string into Sage. We can see
that until exact computation is triggered, an algebraic number keeps
track of the computation steps used to produce that number:

Note that the verify=True argument to sage_input will always trigger
exact computation, so running sage_input twice in a row on the same number
will actually give different answers. In the following, running sage_input
on n will also trigger exact computation on rt2, as you can see by the
fact that the third output is different than the first:

We can convert elements of QQbar and AA into the following
types: float, complex, RDF, CDF, RR, CC,
RIF, CIF, ZZ, and QQ, with a few exceptions. (For the
arbitrary-precision types, RR, CC, RIF, and CIF, it
can convert into a field of arbitrary precision.)

Converting from QQbar to a real type (float, RDF, RR,
RIF, ZZ, or QQ) succeeds only if the QQbar is actually
real (has an imaginary component of exactly zero). Converting from
either AA or QQbar to ZZ or QQ succeeds only if the
number actually is an integer or rational. If conversion fails, a
ValueError will be raised.

An AlgebraicNumber or AlgebraicReal is a wrapper around an
ANDescr object. ANDescr is an abstract base class, which should
never be directly instantiated; its concrete subclasses are ANRational,
ANBinaryExpr, ANUnaryExpr, ANRoot, and ANExtensionElement.
ANDescr and all of its subclasses are for internal use, and should not
be used directly.

The subclass of ANDescr that represents a number field
element in terms of a specific generator. Consists of a polynomial
with rational coefficients in terms of the generator, and the
generator itself, an AlgebraicGenerator.

Whether this is a root in \(\overline{\QQ}\) (rather than \(\mathbf{A}\)).
Note that this may return True even if the root is actually real, as
the second example shows; it does not trigger exact computation to
see if the root is real.

Given a polynomial with algebraic coefficients and an interval
enclosing exactly one root of the polynomial, constructs
an algebraic real representation of that root.

The polynomial need not be irreducible, or even squarefree; but
if the given root is a multiple root, its multiplicity must be
specified. (IMPORTANT NOTE: Currently, multiplicity-\(k\) roots
are handled by taking the \((k-1)\)-st derivative of the polynomial.
This means that the interval must enclose exactly one root
of this derivative.)

The conditions on the arguments (that the interval encloses exactly
one root, and that multiple roots match the given multiplicity)
are not checked; if they are not satisfied, an error may be
thrown (possibly later, when the algebraic number is used),
or wrong answers may result.

Note that if you are constructing multiple roots of a single
polynomial, it is better to use QQbar.common_polynomial
to get a shared polynomial.

poly_degree - default: 2 - degree of the random polynomial over
the integers of which the returned algebraic number is a root. This
is not necessarily the degree of the minimal polynomial of the
number. Increase this parameter to achieve a greater diversity of
algebraic numbers, at a cost of greater computation time. You can
also vary the distribution of the coefficients but that will not vary
the degree of the extension containing the element.

args, kwds - arguments and keywords passed to the random
number generator for elements of ZZ, the integers. See
random_element() for
details, or see example below.

A polynomial with degree between 1 and poly_degree,
with random integer coefficients is created. A root of this
polynomial is chosen at random. The default degree is
2 and the integer coefficients come from a distribution
heavily weighted towards \(0, \pm 1, \pm 2\).

Parameters for the distribution of the integer coefficients
of the polynomials can be passed on to the random element method
for integers. For example, current default behavior of this method
returns zero about 15% of the time; if we do not include zero as a
possible coefficient, there will never be a zero constant term, and
thus never a zero root.

Given a generator gen and another generator super, where super
is the result of a tree of union() operations where one of the
leaves is gen, gen.super_poly(super) returns a polynomial
expressing the value of gen in terms of the value of super
(except that if gen is qq_generator, super_poly() always
returns None.)

EXAMPLES:

sage: fromsage.rings.qqbarimportAlgebraicGenerator,ANRoot,qq_generatorsage: _.<y>=QQ['y']sage: x=polygen(QQbar)sage: nf2=NumberField(y^2-2,name='a',check=False)sage: root2=ANRoot(x^2-2,RIF(1,2))sage: gen2=AlgebraicGenerator(nf2,root2)sage: gen2Number Field in a with defining polynomial y^2 - 2 with a in 1.414213562373095?sage: nf3=NumberField(y^2-3,name='a',check=False)sage: root3=ANRoot(x^2-3,RIF(1,2))sage: gen3=AlgebraicGenerator(nf3,root3)sage: gen3Number Field in a with defining polynomial y^2 - 3 with a in 1.732050807568878?sage: gen2_3=gen2.union(gen3)sage: gen2_3Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in 0.5176380902050415?sage: qq_generator.super_poly(gen2)isNoneTruesage: gen2.super_poly(gen2_3)-a^3 + 3*asage: gen3.super_poly(gen2_3)-a^2 + 2

Given generators alpha and beta,
alpha.union(beta) gives a generator for the number field
\(\QQ[\alpha][\beta]\).

EXAMPLES:

sage: fromsage.rings.qqbarimportANRoot,AlgebraicGenerator,qq_generatorsage: _.<y>=QQ['y']sage: x=polygen(QQbar)sage: nf2=NumberField(y^2-2,name='a',check=False)sage: root2=ANRoot(x^2-2,RIF(1,2))sage: gen2=AlgebraicGenerator(nf2,root2)sage: gen2Number Field in a with defining polynomial y^2 - 2 with a in 1.414213562373095?sage: nf3=NumberField(y^2-3,name='a',check=False)sage: root3=ANRoot(x^2-3,RIF(1,2))sage: gen3=AlgebraicGenerator(nf3,root3)sage: gen3Number Field in a with defining polynomial y^2 - 3 with a in 1.732050807568878?sage: gen2.union(qq_generator)isgen2Truesage: qq_generator.union(gen3)isgen3Truesage: gen2.union(gen3)Number Field in a with defining polynomial y^4 - 4*y^2 + 1 with a in 0.5176380902050415?

One problem with this lexicographic ordering is the fact that if
two algebraic numbers have the same real component, that real
component has to be compared for exact equality, which can be
a costly operation. For the special case where both numbers
have the same minimal polynomial, that cost can be avoided,
though (see trac ticket #16964):

It also works for comparison of conjugate roots even in a degenerate
situation where many roots have the same real part. In the following
example, the polynomial p2 is irreducible and all its roots have
real part equal to \(1\):

Given a ComplexField, return the best possible approximation of
this number in that field. Note that if either component is
sufficiently close to the halfway point between two floating-point
numbers in the corresponding RealField, then this will trigger
exact computation, which may be very slow.

Given the complex field field compute an accurate approximation of
this element in that field.

The approximation will be off by at most two ulp’s in each component,
except for components which are very close to zero, which will have an
absolute error at most \(2^{-prec+1}\) where \(prec\) is the precision of
the field.

Given a ComplexIntervalField, compute the best possible
approximation of this number in that field. Note that if
either the real or imaginary parts of this number are
sufficiently close to some floating-point number (and, in
particular, if either is exactly representable in floating-point),
then this will trigger exact computation, which may be very slow.

Implement powering of an algebraic number (an element of QQbar
or AA) by a rational.

This is always a right action.

INPUT:

G – must be QQ

S – the parent on which to act, either AA or QQbar.

Note

To compute x^(a/b), we take the \(b\)’th root of \(x\); then
we take that to the \(a\)’th power. If \(x\) is a negative algebraic
real and \(b\) is odd, take the real \(b\)’th root; otherwise take
the principal \(b\)’th root.

This is the common base class for algebraic numbers (complex
numbers which are the zero of a polynomial in \(\ZZ[x]\)) and algebraic
reals (algebraic numbers which happen to be real).

AlgebraicNumber objects can be created using QQbar (==
AlgebraicNumberField()), and AlgebraicReal objects can be created
using AA (== AlgebraicRealField()). They can be created either by
coercing a rational or a symbolic expression, or by using the
QQbar.polynomial_root() or AA.polynomial_root() method to
construct a particular root of a polynomial with algebraic
coefficients. Also, AlgebraicNumber and AlgebraicReal are closed
under addition, subtraction, multiplication, division (except by
0), and rational powers (including roots), except that for a
negative AlgebraicReal, taking a power with an even denominator returns
an AlgebraicNumber instead of an AlgebraicReal.

AlgebraicNumber and AlgebraicReal objects can be
approximated to any desired precision. They can be compared
exactly; if the two numbers are very close, or are equal, this may
require exact computation, which can be extremely slow.

As long as exact computation is not triggered, computation with
algebraic numbers should not be too much slower than computation with
intervals. As mentioned above, exact computation is triggered
when comparing two algebraic numbers which are very close together.
This can be an explicit comparison in user code, but the following
list of actions (not necessarily complete) can also trigger exact
computation:

Dividing by an algebraic number which is very close to 0.

Using an algebraic number which is very close to 0 as the leading
coefficient in a polynomial.

Taking a root of an algebraic number which is very close to 0.

The exact definition of “very close” is subject to change; currently,
we compute our best approximation of the two numbers using 128-bit
arithmetic, and see if that’s sufficient to decide the comparison.
Note that comparing two algebraic numbers which are actually equal will
always trigger exact computation, unless they are actually the same object.

Given an interval field (real or complex, as appropriate) of
precision \(p\), compute an interval representation of self with
diameter() at most \(2^{-p}\); then round that representation into
the given field. Here diameter() is relative diameter for
intervals not containing 0, and absolute diameter for
intervals that do contain 0; thus, if the returned interval
does not contain 0, it has at least \(p-1\) good bits.

Given a RealIntervalField or
ComplexIntervalField, compute the value of this number
using interval arithmetic of at least the precision of the field,
and return the value in that field. (More precision may be used
in the computation.) The returned interval may be arbitrarily
imprecise, if this number is the result of a sufficiently long
computation chain.

extend - bool (default: True); ignored if self is in QQbar, or
positive in AA. If self is negative in AA, do the following: if True,
return a square root of self in QQbar, otherwise raise a ValueError.

all - bool (default: False); if True, return a list of all square
roots. If False, return just one square root, or raise an ValueError
if self is a negative element of AA and extend=False.

OUTPUT:

Either the principal square root of self, or a list of its
square roots (with the principal one first).

This second example just shows that the program does not care where 0
is defined, it gives the same answer regardless. After all, how many
ways can you square-root zero?

sage: AA(-2).sqrt()1.414213562373095?*Isage: AA(-2).sqrt(all=True)[1.414213562373095?*I, -1.414213562373095?*I]sage: AA(-2).sqrt(extend=False)Traceback (most recent call last):...ValueError: -2 is not a square in AA, being negative. Use extend = True for a square root in QQbar.

If multiple algebraic numbers are created as roots of a single
polynomial, this allows the polynomial and information about
the polynomial to be shared. This reduces work if the polynomial
must be recomputed at higher precision, or if it must be factored.

This class is private, and should only be constructed by
AA.common_polynomial() or QQbar.common_polynomial(), and should
only be used as an argument to AA.polynomial_root() or
QQbar.polynomial_root(). (It does not matter whether you create
the common polynomial with AA.common_polynomial() or
QQbar.common_polynomial().)

Given a RealIntervalField, compute the best possible
approximation of this number in that field. Note that if this
number is sufficiently close to some floating-point number
(and, in particular, if this number is exactly representable in
floating-point), then this will trigger exact computation, which
may be very slow.

Given a RealField, compute the best possible approximation of
this number in that field. Note that if this number is
sufficiently close to the halfway point between two
floating-point numbers in the field (for the default
round-to-nearest mode) or if the number is sufficiently close
to a floating-point number in the field (for directed rounding
modes), then this will trigger exact computation, which may be
very slow.

Given a RealField, compute a good approximation to self in
that field. The approximation will be off by at most two
ulp’s, except for numbers which are very close to 0, which
will have an absolute error at most
2**(-(field.prec()-1)). Also, the rounding mode of the
field is respected.

Given a polynomial with algebraic coefficients and an interval
enclosing exactly one root of the polynomial, constructs
an algebraic real representation of that root.

The polynomial need not be irreducible, or even squarefree; but
if the given root is a multiple root, its multiplicity must be
specified. (IMPORTANT NOTE: Currently, multiplicity-\(k\) roots
are handled by taking the \((k-1)\)-st derivative of the polynomial.
This means that the interval must enclose exactly one root
of this derivative.)

The conditions on the arguments (that the interval encloses exactly
one root, and that multiple roots match the given multiplicity)
are not checked; if they are not satisfied, an error may be
thrown (possibly later, when the algebraic number is used),
or wrong answers may result.

Note that if you are constructing multiple roots of a single
polynomial, it is better to use AA.common_polynomial (or
QQbar.common_polynomial; the two are equivalent) to get a
shared polynomial.

If the interval v (which may be real or complex) includes some
purely real numbers, return v' containing v such that
v'==v'.conjugate(). Otherwise return v unchanged. (Note that if
v'==v'.conjugate(), and v' includes one non-real root of a real
polynomial, then v' also includes the conjugate of that root.
Also note that the diameter of the return value is at most twice
the diameter of the input.)

If the interval v includes some purely real numbers, return
a real interval containing only those real numbers. Otherwise
return v unchanged.

If v includes exactly one root of a real polynomial, and v was
returned by conjugate_expand(), then conjugate_shrink(v) still
includes that root, and is a RealIntervalFieldElement iff the root
in question is real.

l is a list of some sort. fn is a function which maps an element of
l and a precision into an interval (either real or complex) of that
precision, such that for sufficient precision, exactly one element of l
results in an interval containing 0. Returns that one element of l.

intv_fn is a function that takes a precision and returns an
interval of that precision containing some particular root of pol.
(It must return better approximations as the precision increases.)
pol is an irreducible polynomial with rational coefficients.

Given a sequence of elements of either AA or QQbar
(or a mixture), computes a number field containing all of these
elements, these elements as members of that number field, and a
homomorphism from the number field back to AA or
QQbar.

INPUT:

numbers – a number or list of numbers.

minimal – Boolean (default: False). Whether to minimize the
degree of the extension.

prec – integer (default: 53). The number of bit of precision
to guarantee finding real roots.

OUTPUT:

A tuple with the NumberField, the numbers inside the NumberField,
and a homomorphism from the number field back to AA or QQbar.

This may not return the smallest such number field, unless
minimal=True is specified.

If same_field=True is specified, and all of the elements are
from the same field (either AA or QQbar), the generated
homomorphism will map back to that field. Otherwise, if all specified
elements are real, the homomorphism might map back to AA
(and will, if minimal=True is specified), even if the
elements were in QQbar.

Also, a single number can be passed, rather than a sequence; and
any values which are not elements of AA or QQbar
will automatically be coerced to QQbar

This function may be useful for efficiency reasons: doing exact
computations in the corresponding number field will be faster
than doing exact computations directly in AA or QQbar.

EXAMPLES:

We can use this to compute the splitting field of a polynomial.
(Unfortunately this takes an unreasonably long time for non-toy
examples.):

rt3a is a real number in QQbar. Ordinarily, we’d get a homomorphism
to AA (because all elements are real), but if we specify same_field=True,
we’ll get a homomorphism back to QQbar:

sage: number_field_elements_from_algebraics(rt3a)(Number Field in a with defining polynomial y^2 - 3, a, Ring morphism: From: Number Field in a with defining polynomial y^2 - 3 To: Algebraic Real Field Defn: a |--> 1.732050807568878?)sage: number_field_elements_from_algebraics(rt3a,same_field=True)(Number Field in a with defining polynomial y^2 - 3, a, Ring morphism: From: Number Field in a with defining polynomial y^2 - 3 To: Algebraic Field Defn: a |--> 1.732050807568878?)

We’ve created rt2b in such a way that sage does not initially know
that it’s in a degree-2 extension of \(\QQ\):

sage: number_field_elements_from_algebraics(rt2b,minimal=True)(Number Field in a with defining polynomial y^2 - 2, a, Ring morphism: From: Number Field in a with defining polynomial y^2 - 2 To: Algebraic Real Field Defn: a |--> 1.414213562373095?)