Algebraic numbers

Nemo allows working with exact real and complex algebraic numbers.

The default algebraic number type in Nemo is provided by Calcium. The associated field of algebraic numbers can be constructed using QQBar = algebraic_closure(QQ). We will leave out this line from all code blocks on this page for brevity.

LibraryElement typeParent type
CalciumQQBarFieldElemQQBarField

Important note on performance

The default algebraic number type represents algebraic numbers in canonical form using minimal polynomials. This works well for representing individual algebraic numbers, but it does not provide the best performance for field arithmetic. For fast calculation in $\overline{\mathbb{Q}}$, CalciumField should typically be used instead (see the section on Exact real and complex numbers). Alternatively, to compute in a fixed subfield of $\overline{\mathbb{Q}}$, you may fix a generator $a$ and construct a number field to represent $\mathbb{Q}(a)$.

Algebraic number functionality

Constructing algebraic numbers

Methods to construct algebraic numbers include:

  • Conversion from other numbers and through arithmetic operations
  • Computing the roots of a given polynomial
  • Computing the eigenvalues of a given matrix
  • Random generation
  • Exact trigonometric functions (see later section)
  • Guessing (see later section)

Examples

Arithmetic:

julia> ZZRingElem(QQBar(3))
3

julia> QQFieldElem(QQBar(3) // 2)
3//2

julia> QQBar(-1) ^ (QQBar(1) // 3)
Root 0.500000 + 0.866025*im of x^2 - x + 1

Solving the quintic equation:

julia> R, x = polynomial_ring(QQ, "x")
(Univariate polynomial ring in x over QQ, x)

julia> v = roots(QQBar, x^5-x-1)
5-element Vector{QQBarFieldElem}:
 Root 1.16730 of x^5 - x - 1
 Root 0.181232 + 1.08395*im of x^5 - x - 1
 Root 0.181232 - 1.08395*im of x^5 - x - 1
 Root -0.764884 + 0.352472*im of x^5 - x - 1
 Root -0.764884 - 0.352472*im of x^5 - x - 1

julia> v[1]^5 - v[1] - 1 == 0
true

Computing exact eigenvalues of a matrix:

julia> eigenvalues(QQBar, ZZ[1 1 0; 0 1 1; 1 0 1])
3-element Vector{QQBarFieldElem}:
 Root 2.00000 of x - 2
 Root 0.500000 + 0.866025*im of x^2 - x + 1
 Root 0.500000 - 0.866025*im of x^2 - x + 1

Interface

AbstractAlgebra.Generic.rootsMethod
roots(R::QQBarField, f::ZZPolyRingElem)

Return all the roots of the polynomial f in the field of algebraic numbers R. The output array is sorted in the default sort order for algebraic numbers. Roots of multiplicity higher than one are repeated according to their multiplicity.

source
AbstractAlgebra.Generic.rootsMethod
roots(R::QQBarField, f::QQPolyRingElem)

Return all the roots of the polynomial f in the field of algebraic numbers R. The output array is sorted in the default sort order for algebraic numbers. Roots of multiplicity higher than one are repeated according to their multiplicity.

source
Nemo.eigenvaluesMethod
eigenvalues(R::QQBarField, A::ZZMatrix)
eigenvalues(R::QQBarField, A::QQMatrix)

Return the eigenvalues A in the field of algebraic numbers R. The output array is sorted in the default sort order for algebraic numbers.

source
eigenvalues(L::Field, M::MatElem{T}) where T <: RingElem

Return the eigenvalues of M over the field L.

source
Nemo.eigenvalues_with_multiplicitiesMethod
eigenvalues_with_multiplicities(R::QQBarField, A::ZZMatrix)
eigenvalues_with_multiplicities(R::QQBarField, A::QQMatrix)

Return the eigenvalues A in the field of algebraic numbers R together with their algebraic multiplicities as a vector of tuples. The output array is sorted in the default sort order for algebraic numbers.

source
eigenvalues_with_multiplicities(L::Field, M::MatElem{T}) where T <: RingElem

Return the eigenvalues of M over the field L together with their algebraic multiplicities as a vector of tuples.

source
Nemo.eigenvaluesMethod
eigenvalues(R::QQBarField, A::ZZMatrix)
eigenvalues(R::QQBarField, A::QQMatrix)

Return the eigenvalues A in the field of algebraic numbers R. The output array is sorted in the default sort order for algebraic numbers.

source
eigenvalues(L::Field, M::MatElem{T}) where T <: RingElem

Return the eigenvalues of M over the field L.

source
Nemo.eigenvalues_with_multiplicitiesMethod
eigenvalues_with_multiplicities(R::QQBarField, A::ZZMatrix)
eigenvalues_with_multiplicities(R::QQBarField, A::QQMatrix)

Return the eigenvalues A in the field of algebraic numbers R together with their algebraic multiplicities as a vector of tuples. The output array is sorted in the default sort order for algebraic numbers.

source
eigenvalues_with_multiplicities(L::Field, M::MatElem{T}) where T <: RingElem

Return the eigenvalues of M over the field L together with their algebraic multiplicities as a vector of tuples.

source
Base.randMethod
rand(R::QQBarField; degree::Int, bits::Int, randtype::Symbol=:null)

Return a random algebraic number with degree up to degree and coefficients up to bits in size. By default, both real and complex numbers are generated. Set the optional randtype to :real or :nonreal to generate a specific type of number. Note that nonreal numbers require degree at least 2.

source

Numerical evaluation

Examples

Algebraic numbers can be evaluated numerically to arbitrary precision by converting to real or complex Arb fields:

julia> RR = ArbField(64); RR(sqrt(QQBar(2)))
[1.414213562373095049 +/- 3.45e-19]

julia> CC = AcbField(32); CC(QQBar(-1) ^ (QQBar(1) // 4))
[0.707106781 +/- 2.74e-10] + [0.707106781 +/- 2.74e-10]*im

Minimal polynomials, conjugates, and properties

Examples

Retrieving the minimal polynomial and algebraic conjugates of a given algebraic number:

julia> minpoly(polynomial_ring(ZZ, "x")[1], QQBar(1+2im))
x^2 - 2*x + 5

julia> conjugates(QQBar(1+2im))
2-element Vector{QQBarFieldElem}:
 Root 1.00000 + 2.00000*im of x^2 - 2x + 5
 Root 1.00000 - 2.00000*im of x^2 - 2x + 5

Interface

Base.iszeroMethod
iszero(x::QQBarFieldElem)

Return whether x is the number 0.

source
Base.isoneMethod
isone(x::QQBarFieldElem)

Return whether x is the number 1.

source
Base.isrealMethod
isreal(x::QQBarFieldElem)

Return whether x is a real number.

source
AbstractAlgebra.minpolyMethod
minpoly(R::ZZPolyRing, x::QQBarFieldElem)

Return the minimal polynomial of x as an element of the polynomial ring R.

source
AbstractAlgebra.minpolyMethod
minpoly(R::ZZPolyRing, x::QQBarFieldElem)

Return the minimal polynomial of x as an element of the polynomial ring R.

source
Nemo.conjugatesMethod
conjugates(a::QQBarFieldElem)

Return all the roots of the polynomial f in the field of algebraic numbers R. The output array is sorted in the default sort order for algebraic numbers.

source
Base.denominatorMethod
denominator(x::QQBarFieldElem)

Return the denominator of x, defined as the leading coefficient of the minimal polynomial of x. The result is returned as an ZZRingElem.

source
Base.numeratorMethod
numerator(x::QQBarFieldElem)

Return the numerator of x, defined as x multiplied by its denominator. The result is an algebraic integer.

source
Nemo.heightMethod
height(x::QQBarFieldElem)

Return the height of the algebraic number x. The result is an ZZRingElem integer.

source
Nemo.height_bitsMethod
height_bits(x::QQBarFieldElem)

Return the height of the algebraic number x measured in bits. The result is a Julia integer.

source

Complex parts

Examples

julia> real(sqrt(QQBar(1im)))
Root 0.707107 of 2x^2 - 1

julia> abs(sqrt(QQBar(1im)))
Root 1.00000 of x - 1

julia> floor(sqrt(QQBar(1000)))
Root 31.0000 of x - 31

julia> sign(QQBar(-10-20im))
Root -0.447214 - 0.894427*im of 5x^4 + 6x^2 + 5

Interface

Base.realMethod
real(a::QQBarFieldElem)

Return the real part of a.

source
Base.imagMethod
imag(a::QQBarFieldElem)

Return the imaginary part of a.

source
Base.absMethod
abs(a::QQBarFieldElem)

Return the absolute value of a.

source
Base.abs2Method
abs2(a::QQBarFieldElem)

Return the squared absolute value of a.

source
Base.conjMethod
conj(a::QQBarFieldElem)

Return the complex conjugate of a.

source
Base.signMethod
sign(a::QQBarFieldElem)

Return the complex sign of a, defined as zero if a is zero and as $a / |a|$ otherwise.

source
Nemo.csgnMethod
csgn(a::QQBarFieldElem)

Return the extension of the real sign function taking the value 1 strictly in the right half plane, -1 strictly in the left half plane, and the sign of the imaginary part when on the imaginary axis. Equivalently, $\operatorname{csgn}(x) = x / \sqrt{x^2}$ except that the value is 0 at zero. The value is returned as a Julia integer.

source
Nemo.sign_realMethod
sign_real(a::QQBarFieldElem)

Return the sign of the real part of a as a Julia integer.

source
Nemo.sign_imagMethod
sign_imag(a::QQBarFieldElem)

Return the sign of the imaginary part of a as a Julia integer.

source

Comparing algebraic numbers

The operators == and != check exactly for equality.

We provide various comparison functions for ordering algebraic numbers:

  • Standard comparison for real numbers (<, isless)
  • Real parts
  • Imaginary parts
  • Absolute values
  • Absolute values of real or imaginary parts
  • Root sort order

The standard comparison will throw if either argument is nonreal.

The various comparisons for complex parts are provided as separate operations since these functions are far more efficient than explicitly computing the complex parts and then doing real comparisons.

The root sort order is a total order for complex algebraic numbers used to order the output of roots and conjugates canonically. We define this order as follows: real roots come first, in descending order. Nonreal roots are subsequently ordered first by real part in descending order, then in ascending order by the absolute value of the imaginary part, and then in descending order of the sign of the imaginary part. This implies that complex conjugate roots are adjacent, with the root in the upper half plane first.

Examples

julia> 1 < sqrt(QQBar(2)) < QQBar(3)//2
true

julia> x = QQBar(3+4im)
Root 3.00000 + 4.00000*im of x^2 - 6x + 25

julia> is_equal_abs(x, -x)
true

julia> is_equal_abs_imag(x, 2-x)
true

julia> is_less_real(x, x // 2)
false

Interface

Nemo.is_equal_realMethod
is_equal_real(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the real parts of a and b.

source
Nemo.is_equal_imagMethod
is_equal_imag(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the imaginary parts of a and b.

source
Nemo.is_equal_absMethod
is_equal_abs(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the absolute values of a and b.

source
Nemo.is_equal_abs_realMethod
is_equal_abs_real(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the absolute values of the real parts of a and b.

source
Nemo.is_equal_abs_imagMethod
is_equal_abs_imag(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the absolute values of the imaginary parts of a and b.

source
Nemo.is_less_realMethod
is_less_real(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the real parts of a and b.

source
Nemo.is_less_imagMethod
is_less_imag(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the imaginary parts of a and b.

source
Nemo.is_less_absMethod
is_less_abs(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the absolute values of a and b.

source
Nemo.is_less_abs_realMethod
is_less_abs_real(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the absolute values of the real parts of a and b.

source
Nemo.is_less_abs_imagMethod
is_less_abs_imag(a::QQBarFieldElem, b::QQBarFieldElem)

Compares the absolute values of the imaginary parts of a and b.

source

Roots and trigonometric functions

Examples

julia> root(QQBar(2), 5)
Root 1.14870 of x^5 - 2

julia> sinpi(QQBar(7) // 13)
Root 0.992709 of 4096x^12 - 13312x^10 + 16640x^8 - 9984x^6 + 2912x^4 - 364x^2 + 13

julia> tanpi(atanpi(sqrt(QQBar(2)) + 1))
Root 2.41421 of x^2 - 2x - 1

julia> root_of_unity(QQBar, 5)
Root 0.309017 + 0.951057*im of x^4 + x^3 + x^2 + x + 1

julia> root_of_unity(QQBar, 5, 4)
Root 0.309017 - 0.951057*im of x^4 + x^3 + x^2 + x + 1

julia> w = (1 - sqrt(QQBar(-3)))//2
Root 0.500000 - 0.866025*im of x^2 - x + 1

julia> is_root_of_unity(w)
true

julia> is_root_of_unity(w + 1)
false

julia> root_of_unity_as_args(w)
(6, 5)

Interface

Base.sqrtMethod
sqrt(a::QQBarFieldElem; check::Bool=true)

Return the principal square root of a.

source
Nemo.root_of_unityMethod
root_of_unity(C::QQBarField, n::Int)

Return the root of unity $e^{2 \pi i / n}$ as an element of the field of algebraic numbers C.

source
Nemo.root_of_unityMethod
root_of_unity(C::QQBarField, n::Int, k::Int)

Return the root of unity $e^{2 \pi i k / n}$ as an element of the field of algebraic numbers C.

source
Nemo.root_of_unity_as_argsMethod
root_of_unity_as_args(a::QQBarFieldElem)

Return a pair of integers (q, p) such that the given a equals $e^{2 \pi i p / q}$. The denominator q will be minimal, with $0 \le p < q$. Throws if a is not a root of unity.

source
Nemo.exp_pi_iMethod
exp_pi_i(a::QQBarFieldElem)

Return $e^{\pi i a}$ as an algebraic number. Throws if this value is transcendental.

source
Nemo.log_pi_iMethod
log_pi_i(a::QQBarFieldElem)

Return $\log(a) / (\pi i)$ as an algebraic number. Throws if this value is transcendental or undefined.

source
Base.Math.sinpiMethod
sinpi(a::QQBarFieldElem)

Return $\sin(\pi a)$ as an algebraic number. Throws if this value is transcendental.

Examples

julia> QQBar = algebraic_closure(QQ);

julia> x = sinpi(QQBar(1//3))
Root 0.866025 of 4x^2 - 3

julia> sinpi(x)
ERROR: DomainError with Root 0.866025 of 4x^2 - 3:
nonrational algebraic number
source
Base.Math.cospiMethod
cospi(a::QQBarFieldElem)

Return $\cos(\pi a)$ as an algebraic number. Throws if this value is transcendental.

Examples

julia> QQBar = algebraic_closure(QQ);

julia> x = cospi(QQBar(1//6))
Root 0.866025 of 4x^2 - 3

julia> cospi(x)
ERROR: DomainError with Root 0.866025 of 4x^2 - 3:
nonrational algebraic number
source
Base.Math.sincospiMethod
sincospi(a::QQBarFieldElem)

Return $\sin(\pi a)$ and $\cos(\pi a)$ as a pair of algebraic numbers. Throws if either value is transcendental.

Examples

julia> QQBar = algebraic_closure(QQ);

julia> s, c = sincospi(QQBar(1//3))
(Root 0.866025 of 4x^2 - 3, Root 0.500000 of 2x - 1)

julia> sincospi(s)
ERROR: DomainError with Root 0.866025 of 4x^2 - 3:
nonrational algebraic number
source
Base.Math.tanpiMethod
tanpi(a::QQBarFieldElem)

Return $\tan(\pi a)$ as an algebraic number. Throws if this value is transcendental or undefined.

source
Nemo.asinpiMethod
asinpi(a::QQBarFieldElem)

Return $\operatorname{asin}(a) / \pi$ as an algebraic number. Throws if this value is transcendental.

source
Nemo.acospiMethod
acospi(a::QQBarFieldElem)

Return $\operatorname{acos}(a) / \pi$ as an algebraic number. Throws if this value is transcendental.

source
Nemo.atanpiMethod
atanpi(a::QQBarFieldElem)

Return $\operatorname{atan}(a) / \pi$ as an algebraic number. Throws if this value is transcendental or undefined.

source

Guessing

Examples

An algebraic number can be recovered from a numerical value:

julia> RR = RealField(); guess(QQBar, RR("1.41421356 +/- 1e-6"), 2)
Root 1.41421 of x^2 - 2

Warning: the input should be an enclosure. If you have a floating-point approximation, you should add an error estimate; otherwise, at best the only algebraic number that can be guessed is the binary floating-point number itself, at worst no guess is possible.

julia> RR = RealField();

julia> x = RR(0.1)       # note: 53-bit binary approximation of 1//10 without radius
[0.10000000000000000555 +/- 1.12e-21]

julia> guess(QQBar, x, 1)
ERROR: No suitable algebraic number found

julia> guess(QQBar, x + RR("+/- 1e-10"), 1)
Root 0.100000 of 10x - 1

Interface

Nemo.guessFunction
guess(R::QQBarField, x::AcbFieldElem, maxdeg::Int, maxbits::Int=0)
guess(R::QQBarField, x::ArbFieldElem, maxdeg::Int, maxbits::Int=0)
guess(R::QQBarField, x::ComplexFieldElem, maxdeg::Int, maxbits::Int=0)
guess(R::QQBarField, x::RealFieldElem, maxdeg::Int, maxbits::Int=0)

Try to reconstruct an algebraic number from a given numerical enclosure x. The algorithm looks for candidates up to degree maxdeg and with coefficients up to size maxbits (which defaults to the precision of x if not given). Throws if no suitable algebraic number can be found.

Guessing typically requires high precision to succeed, and it does not make much sense to call this function with input precision smaller than $O(maxdeg \cdot maxbits)$. If this function succeeds, then the output is guaranteed to be contained in the enclosure x, but failure does not prove that such an algebraic number with the specified parameters does not exist.

This function does a single iteration with the target parameters. For best performance, one should invoke this function repeatedly with successively larger parameters when the size of the intended solution is unknown or may be much smaller than a worst-case bound.

source