Exact real and complex numbers
Exact real and complex numbers are provided by Calcium. Internally, a number $z$ is represented as an element of an extension field of the rational numbers. That is,
\[z \in \mathbb{Q}(a_1,\ldots,a_n)\]
where $a_1, \ldots, a_n$ are symbolically defined algebraic or transcendental real or complex numbers such as $\pi$, $\sqrt{2}$ or $e^{\sqrt{2} \pi i}$. The user does not normally need to worry about the details of the internal representation; Calcium constructs extension numbers and fields automatically as needed to perform operations.
The user must create a CalciumField
instance which represents the mathematical domain $\mathbb{C}$. This parent object holds a cache of extension numbers and fields used to represent individual elements. It also stores various options for evaluation (documented further below).
Library | Element type | Parent type |
---|---|---|
Calcium | CalciumFieldElem | CalciumField |
Please note the following:
It is in the nature of exact complex arithmetic that some operations must be implemented using incomplete heuristics. For example, testing whether an element is zero will not always succeed. When Calcium is unable to perform a task, Nemo will throw an exception. This ensures that Calcium fields behave exactly and never silently return wrong results.
Calcium elements can optionally hold special non-numerical values:
Unsigned infinity $\hat \infty$
Signed infinities ($\pm \infty$, $\pm i \infty$, and more generally $e^{i \theta} \cdot \infty$)
Undefined
Unknown
By default, such special values are disallowed so that a
CalciumField
represents the mathematical field $\mathbb{C}$, and any operation that would result in a special value (for example, $1 / 0 = \hat \infty$) will throw an exception. To allow special values, passextended=true
to theCalciumField
constructor.CalciumField
instances only support single-threaded use. You must create a separate parent object for each thread to do parallel computation.When performing an operation involving two
CalciumFieldElem
operands with different parent objects, Nemo will arbitrarily coerce the operands (and hence the result) to one of the parents.
Calcium field options
The CalciumField
parent stores various options that affect simplification power, performance, or appearance. The user can override any of the default values using C = CalciumField(options=dict)
where dict
is a dictionary with Symbol => Int
pairs. To retrieve the option values as a dictionary (including any default values not set by the user), call options(C)
.
The following options are supported:
Option | Explanation |
---|---|
:verbose | Enable debug output |
:print_flags | Flags controlling print style |
:mpoly_ord | Monomial order for polynomials |
:prec_limit | Precision limit for numerical evaluation |
:qqbar_deg_limit | Degree limit for algebraic numbers |
:low_prec | Initial precision for numerical evaluation |
:smooth_limit | Factor size limit for smooth integer factorization |
:lll_prec | Precision for integer relation detection |
:pow_limit | Maximum exponent for in-field powering |
:use_gb | Enable Gröbner basis computation |
:gb_length_limit | Maximum ideal basis length during Gröbner basis computation |
:gb_poly_length_limit | Maximum polynomial length during Gröbner basis computation |
:gb_poly_bits_limit | Maximum bit size during Gröbner basis computation |
:gb_vieta_limit | Maximum degree to use Vieta's formulas |
:trig_form | Default form of trigonometric functions |
An important function of these options is to control how hard Calcium will try to find an answer before it gives up. For example:
Setting
:prec_limit => 65536
will allow Calcium to use up to 65536 bits of precision (instead of the default 4096) to prove inequalities.Setting
:qqbar_deg_limit => typemax(Int)
(instead of the default 120) will force most calculations involving algebraic numbers to run to completion, no matter how long this will take.Setting
:use_gb => 0
(instead of the default 1) disables use of Gröbner bases. In general, this will negatively impact Calcium's ability to simplify field elements and prove equalities, but it can speed up calculations where Gröbner bases are unnecessary.
For a detailed explanation, refer to the following section in the Calcium documentation: https://fredrikj.net/calcium/ca.html#context-options
Basic examples
julia> C = CalciumField()
Exact complex field
julia> exp(C(pi) * C(1im)) + 1
0
julia> log(C(-1))
3.14159*I {a*b where a = 3.14159 [Pi], b = I [b^2+1=0]}
julia> log(C(-1)) ^ 2
-9.86960 {-a^2 where a = 3.14159 [Pi], b = I [b^2+1=0]}
julia> log(C(10)^23) // log(C(100))
11.5000 {23/2}
julia> 4*atan(C(1)//5) - atan(C(1)//239) == C(pi)//4
true
julia> Cx, x = polynomial_ring(C, "x")
(Univariate polynomial ring in x over exact complex field, x)
julia> (a, b) = (sqrt(C(2)), sqrt(C(3)))
(1.41421 {a where a = 1.41421 [a^2-2=0]}, 1.73205 {a where a = 1.73205 [a^2-3=0]})
julia> (x-a-b)*(x-a+b)*(x+a-b)*(x+a+b)
x^4 + -10*x^2 + 1
Conversions and numerical evaluation
Calcium numbers can created from integers (ZZ
), rationals (QQ
) and algebraic numbers (algebraic_closure(QQ)
), and through the application of arithmetic operations and transcendental functions.
Calcium numbers can be converted to integers, rational and algebraic fields provided that the values are integer, rational or algebraic. An exception is thrown if the value does not belong to the target domain, if Calcium is unable to prove that the value belongs to the target domain, or if Calcium is unable to compute the explicit value because of evaluation limits.
julia> QQ(C(1))
1
julia> algebraic_closure(QQ)(sqrt(C(2)) // 2)
Root 0.707107 of 2x^2 - 1
julia> QQ(C(pi))
ERROR: unable to convert to a rational number
[...]
julia> QQ(C(10) ^ C(10^9))
ERROR: unable to convert to a rational number
[...]
To compute arbitrary-precision numerical enclosures, convert to ArbField
or AcbField
:
julia> CC = AcbField(64)
Complex Field with 64 bits of precision and error bounds
julia> CC(exp(C(1im)))
[0.54030230586813971740 +/- 9.37e-22] + [0.84147098480789650665 +/- 2.51e-21]*im
The constructor
(R::AcbField)(a::CalciumFieldElem; parts::Bool=false)
returns an enclosure of the complex number a. It attempts to obtain a relative accuracy of prec bits where prec is the precision of the target field, but it is not guaranteed that this goal is achieved.
If parts is set to true, it attempts to achieve the target accuracy for both real and imaginary parts. This can be significantly more expensive if one part is smaller than the other, or if the number is nontrivially purely real or purely imaginary (in which case an exact proof attempt is made).
julia> x = sin(C(1), form=:exponential)
0.841471 + 0e-24*I {(-a^2*b+b)/(2*a) where a = 0.540302 + 0.841471*I [Exp(1.00000*I {b})], b = I [b^2+1=0]}
julia> AcbField(64)(x)
[0.84147098480789650665 +/- 2.51e-21] + [+/- 4.77e-29]*im
julia> AcbField(64)(x, parts=true)
[0.84147098480789650665 +/- 2.51e-21]
The constructor
(R::ArbField)(a::CalciumFieldElem; check::Bool=true)
returns a real enclosure. If check is set to true (default), the number a is verified to be real, and an exception is thrown if this cannot be determined. With check set to false, this function returns an enclosure of the real part of a without checking that the imaginary part is zero. This can be significantly faster.
Comparisons and properties
Except where otherwise noted, predicate functions such as iszero
, ==
, <
and isreal
act on the mathematical values of Calcium field elements. For example, although evaluating $x = \sqrt{2} \sqrt{3}$ and $y = \sqrt{6}$ results in different internal representations ($x \in \mathbb{Q}(\sqrt{3}, \sqrt{2})$ and $y \in \mathbb{Q}(\sqrt{6})$), the numbers compare as equal:
julia> x = sqrt(C(2)) * sqrt(C(3))
2.44949 {a*b where a = 1.73205 [a^2-3=0], b = 1.41421 [b^2-2=0]}
julia> y = sqrt(C(6))
2.44949 {a where a = 2.44949 [a^2-6=0]}
julia> x == y
true
julia> iszero(x - y)
true
julia> isinteger(x - y)
true
Predicate functions return true if the property is provably true and false if the property if provably false. If Calcium is unable to prove the truth value, an exception is thrown. For example, with default settings, Calcium is currently able to prove that $e^{e^{-1000}} \ne 1$, but it fails to prove $e^{e^{-3000}} \ne 1$:
julia> x = exp(exp(C(-1000)))
1.00000 {a where a = 1.00000 [Exp(5.07596e-435 {b})], b = 5.07596e-435 [Exp(-1000)]}
julia> x == 1
false
julia> x = exp(exp(C(-3000)))
1.00000 {a where a = 1.00000 [Exp(1.30784e-1303 {b})], b = 1.30784e-1303 [Exp(-3000)]}
julia> x == 1
ERROR: Unable to perform operation (failed deciding truth of a predicate): isequal
[...]
In this case, we can get an answer by allowing a higher working precision:
julia> C2 = CalciumField(options=Dict(:prec_limit => 10^5));
julia> exp(exp(C2(-3000))) == 1
false
Real numbers can be ordered and sorted the usual way. We illustrate finding square roots that are well-approximated by integers:
julia> sort([sqrt(C(n)) for n=0:10], by=x -> abs(x - floor(x + C(1)//2)))
11-element Vector{CalciumFieldElem}:
0
1
2
3
3.16228 {a where a = 3.16228 [a^2-10=0]}
2.82843 {2*a where a = 1.41421 [a^2-2=0]}
2.23607 {a where a = 2.23607 [a^2-5=0]}
1.73205 {a where a = 1.73205 [a^2-3=0]}
2.64575 {a where a = 2.64575 [a^2-7=0]}
1.41421 {a where a = 1.41421 [a^2-2=0]}
2.44949 {a where a = 2.44949 [a^2-6=0]}
As currently implemented, order comparisons involving nonreal numbers yield false (in both directions) rather than throwing an exception:
julia> C(1im) < C(1im)
false
julia> C(1im) > C(1im)
false
This behavior may be changed or may become configurable in the future.
Interface
Base.iszero
— Methodiszero(a::CalciumFieldElem)
Return whether a
is the number 0.
Base.isone
— Methodisone(a::CalciumFieldElem)
Return whether a
is the number 1.
Nemo.is_algebraic
— Methodis_algebraic(a::CalciumFieldElem)
Return whether a
is an algebraic number.
Nemo.is_rational
— Methodis_rational(a::CalciumFieldElem)
Return whether a
is a rational number.
Base.isinteger
— Methodisinteger(a::CalciumFieldElem)
Return whether a
is an integer.
Base.isreal
— Methodisreal(a::CalciumFieldElem)
Return whether a
is a real number. This returns false
if a
is a pure real infinity.
Nemo.is_imaginary
— Methodis_imaginary(a::CalciumFieldElem)
Return whether a
is an imaginary number. This returns false
if a
is a pure imaginary infinity.
Infinities and special values
By default, CalciumField
does not permit creating values that are not numbers, and any non-number value (unsigned infinity, signed infinity, Undefined) will result in an exception. This also applies to the special value Unknown, used in situations where Calcium is unable to prove that a value is a number. To enable special values, use extended=true
.
julia> C = CalciumField()
Exact complex field
julia> 1 // C(0)
ERROR: DomainError with UnsignedInfinity:
Non-number result
[...]
julia> Cext = CalciumField(extended=true)
Exact complex field (extended)
julia> 1 // Cext(0)
UnsignedInfinity
Note that special values do not satisfy the properties of a mathematical ring or field. You will likely get meaningless results if you put infinities in matrices or polynomials.
Nemo.unsigned_infinity
— Methodunsigned_infinity(C::CalciumField)
Return unsigned infinity ($\hat \infty$) as an element of C
. This throws an exception if C
does not allow special values.
Nemo.infinity
— Methodinfinity(C::CalciumField)
Return positive infinity ($+\infty$) as an element of C
. This throws an exception if C
does not allow special values.
Nemo.infinity
— Methodinfinity(a::CalciumFieldElem)
Return the signed infinity ($a \cdot \infty$). This throws an exception if the parent of a
does not allow special values.
Nemo.undefined
— Methodundefined(C::CalciumField)
Return the special value Undefined as an element of C
. This throws an exception if C
does not allow special values.
Nemo.unknown
— Methodunknown(C::CalciumField)
Return the special meta-value Unknown as an element of C
. This throws an exception if C
does not allow special values.
Nemo.is_number
— Methodis_number(a::CalciumFieldElem)
Return whether a
is a number, i.e. not an infinity or undefined.
Nemo.is_undefined
— Methodis_undefined(a::CalciumFieldElem)
Return whether a
is the special value Undefined.
Base.isinf
— Methodisinf(a::CalciumFieldElem)
Return whether a
is any infinity (signed or unsigned).
Nemo.is_uinf
— Methodis_uinf(a::CalciumFieldElem)
Return whether a
is unsigned infinity.
Nemo.is_signed_inf
— Methodis_signed_inf(a::CalciumFieldElem)
Return whether a
is any signed infinity.
Nemo.is_unknown
— Methodis_unknown(a::CalciumFieldElem)
Return whether a
is the special value Unknown. This is a representation property and not a mathematical predicate.
Complex parts
Functions for computing components of real and complex numbers will perform automatic symbolic simplifications in special cases. In general, such operations will introduce new extension numbers.
julia> real(C(2+3im))
2
julia> sign(C(2im))
1.00000*I {a where a = I [a^2+1=0]}
julia> sign(C(2+3im))
0.554700 + 0.832050*I {a where a = 0.554700 + 0.832050*I [13*a^4+10*a^2+13=0]}
julia> angle(C(2+2im))
0.785398 {(a)/4 where a = 3.14159 [Pi]}
julia> angle(C(2+3im))
0.982794 {a where a = 0.982794 [Arg(2.00000 + 3.00000*I {3*b+2})], b = I [b^2+1=0]}
julia> angle(C(2+3im)) == atan(C(3)//2)
true
julia> floor(C(pi) ^ 100)
5.18785e+49 {51878483143196131920862615246303013562686760680405}
julia> ZZ(floor(C(pi) ^ 100))
51878483143196131920862615246303013562686760680405
Interface
Base.real
— Methodreal(a::CalciumFieldElem)
Return the real part of a
.
Base.imag
— Methodimag(a::CalciumFieldElem)
Return the imaginary part of a
.
Base.angle
— Methodangle(a::CalciumFieldElem)
Return the complex argument of a
.
Nemo.csgn
— Methodcsgn(a::CalciumFieldElem)
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.
Base.sign
— Methodsign(a::CalciumFieldElem)
Return the complex sign of a
, defined as zero if a
is zero and as $a / |a|$ for any other complex number. This function also extracts the sign when a
is a signed infinity.
Base.abs
— Methodabs(a::CalciumFieldElem)
Return the absolute value of a
.
Base.conj
— Methodconj(a::CalciumFieldElem; form::Symbol=:default)
Return the complex conjugate of a
. The optional form
argument allows specifying the representation. In :shallow
form, $\overline{a}$ is introduced as a new extension number if it no straightforward simplifications are possible. In :deep
form, complex conjugation is performed recursively.
Base.floor
— Methodfloor(a::CalciumFieldElem)
Return the floor function of a
.
Base.ceil
— Methodceil(a::CalciumFieldElem)
Return the ceiling function of a
.
Elementary and special functions
Elementary and special functions generally create new extension numbers. In special cases, simplifications occur automatically.
julia> exp(C(1))
2.71828 {a where a = 2.71828 [Exp(1)]}
julia> exp(C(0))
1
julia> atan(C(1))
0.785398 {(a)/4 where a = 3.14159 [Pi]}
julia> cos(C(1))^2 + sin(C(1))^2
1
julia> log(1 // exp(sqrt(C(2))+1)) == -sqrt(C(2)) - 1
true
julia> gamma(C(2+3im))
-0.0823953 + 0.0917743*I {a where a = -0.0823953 + 0.0917743*I [Gamma(2.00000 + 3.00000*I {3*b+2})], b = I [b^2+1=0]}
julia> gamma(C(5) // 2)
1.32934 {(3*a)/4 where a = 1.77245 [Sqrt(3.14159 {b})], b = 3.14159 [Pi]}
julia> erf(C(1))
0.842701 {a where a = 0.842701 [Erf(1)]}
julia> erf(C(1)) + erfc(C(1))
1
Some functions allow representing the result in different forms:
julia> s1 = sin(C(1))
0.841471 + 0e-24*I {(-a^2*b+b)/(2*a) where a = 0.540302 + 0.841471*I [Exp(1.00000*I {b})], b = I [b^2+1=0]}
julia> s2 = sin(C(1), form=:direct)
0.841471 {a where a = 0.841471 [Sin(1)]}
julia> s3 = sin(C(1), form=:exponential)
0.841471 + 0e-24*I {(-a^2*b+b)/(2*a) where a = 0.540302 + 0.841471*I [Exp(1.00000*I {b})], b = I [b^2+1=0]}
julia> s4 = sin(C(1), form=:tangent)
0.841471 {(2*a)/(a^2+1) where a = 0.546302 [Tan(0.500000 {1/2})]}
julia> s1 == s2 == s3 == s4
true
julia> isreal(s1) && isreal(s2) && isreal(s3) && isreal(s4)
true
The exponential form is currently used by default since it tends to be the most useful for symbolic simplification. The :direct
and :tangent
forms are likely to be better for numerical evaluation. The default behavior of trigonometric functions can be changed using the :trig_form
option of CalciumField
.
Proving equalities involving transcendental function values is a difficult problem in general. Calcium will sometimes fail even in elementary cases. Here is an example of two constant trigonometric identities where the first succeeds and the second fails:
julia> a = sqrt(C(2)) + 1;
julia> cos(a) + cos(2*a) + cos(3*a) == sin(7*a//2)//(2*sin(a//2)) - C(1)//2
true
julia> sin(3*a) == 4 * sin(a) * sin(C(pi)//3 - a) * sin(C(pi)//3 + a)
ERROR: Unable to perform operation (failed deciding truth of a predicate): isequal
[...]
A possible workaround is to fall back on a numerical comparison:
julia> abs(cos(a) + cos(2*a) + cos(3*a) - (sin(7*a//2)//(2*sin(a//2)) - C(1)//2)) <= C(10)^-100
true
Of course, this is not a rigorous proof that the numbers are equal, and CalciumField
is overkill here; it would be far more efficient to use ArbField
directly to check that the numbers are approximately equal.
Interface
Nemo.const_pi
— Methodconst_pi(C::CalciumField)
Return the constant $\pi$ as an element of C
.
Nemo.const_euler
— Methodconst_euler(C::CalciumField)
Return Euler's constant $\gamma$ as an element of C
.
Nemo.onei
— Methodonei(C::CalciumField)
Return the imaginary unit $i$ as an element of C
.
Base.sqrt
— MethodBase.sqrt(a::CalciumFieldElem; check::Bool=true)
Return the principal square root of a
.
Base.exp
— Methodexp(a::CalciumFieldElem)
Return the exponential function of a
.
Base.log
— Methodlog(a::CalciumFieldElem)
Return the natural logarithm of a
.
Nemo.pow
— Methodpow(a::CalciumFieldElem, b::Int; form::Symbol=:default)
Return a raised to the integer power b
. The optional form
argument allows specifying the representation. In :default
form, this is equivalent to a ^ b
, which may create a new extension number $a^b$ if the exponent b
is too large (as determined by the parent option :pow_limit
or :prec_limit
depending on the case). In :arithmetic
form, the exponentiation is performed arithmetically in the field of a
, regardless of the size of the exponent b
.
Base.sin
— Methodsin(a::CalciumFieldElem; form::Symbol=:default)
Return the sine of a
. The optional form
argument allows specifying the representation. In :default
form, the result is determined by the :trig_form
option of the parent object. In :exponential
form, the value is represented using complex exponentials. In :tangent
form, the value is represented using tangents. In :direct
form, the value is represented directly using a sine or cosine.
Base.cos
— Methodcos(a::CalciumFieldElem; form::Symbol=:default)
Return the cosine of a
. The optional form
argument allows specifying the representation. In :default
form, the result is determined by the :trig_form
option of the parent object. In :exponential
form, the value is represented using complex exponentials. In :tangent
form, the value is represented using tangents. In :direct
form, the value is represented directly using a sine or cosine.
Base.tan
— Methodtan(a::CalciumFieldElem; form::Symbol=:default)
Return the tangent of a
. The optional form
argument allows specifying the representation. In :default
form, the result is determined by the :trig_form
option of the parent object. In :exponential
form, the value is represented using complex exponentials. In :direct
or :tangent
form, the value is represented directly using tangents. In :sine_cosine
form, the value is represented using sines or cosines.
Base.atan
— Methodatan(a::CalciumFieldElem; form::Symbol=:default)
Return the inverse tangent of a
. The optional form
argument allows specifying the representation. In :default
form, the result is determined by the :trig_form
option of the parent object. In :logarithm
form, the value is represented using complex logarithms. In :direct
or :arctangent
form, the value is represented directly using arctangents.
Base.asin
— Methodasin(a::CalciumFieldElem; form::Symbol=:default)
Return the inverse sine of a
. The optional form
argument allows specifying the representation. In :default
form, the result is determined by the :trig_form
option of the parent object. In :logarithm
form, the value is represented using complex logarithms. In :direct
form, the value is represented directly using an inverse sine or cosine.
Base.acos
— Methodacos(a::CalciumFieldElem; form::Symbol=:default)
Return the inverse cosine of a
. The optional form
argument allows specifying the representation. In :default
form, the result is determined by the :trig_form
option of the parent object. In :logarithm
form, the value is represented using complex logarithms. In :direct
form, the value is represented directly using an inverse sine or cosine.
Nemo.gamma
— Methodgamma(a::CalciumFieldElem)
Return the gamma function of a
.
Nemo.erf
— Methoderf(a::CalciumFieldElem)
Return the error function of a
.
Nemo.erfi
— Methoderfi(a::CalciumFieldElem)
Return the imaginary error function of a
.
Nemo.erfc
— Methoderfc(a::CalciumFieldElem)
Return the complementary error function of a
.
Rewriting and simplification
Nemo.complex_normal_form
— Methodcomplex_normal_form(a::CalciumFieldElem, deep::Bool=true)
Returns the input rewritten using standardizing transformations over the complex numbers:
Elementary functions are rewritten in terms of exponentials, roots and logarithms.
Complex parts are rewritten using logarithms, square roots, and (deep) complex conjugates.
Algebraic numbers are rewritten in terms of cyclotomic fields where applicable.
If deep is set, the rewriting is applied recursively to the tower of extension numbers; otherwise, the rewriting is only applied to the top-level extension numbers.
The result is not a normal form in the strong sense (the same number can have many possible representations even after applying this transformation), but this transformation can nevertheless be a useful heuristic for simplification.