Exact values in x

For exact classical coefficients, use Exact():

using QRecoupling
c = q6j(Exact(), 1, 1, 1, 1, 1, 1)
r = q3j(Exact(), 1//2, 1//2, 0, 1//2, -1//2, 0)
(c, r)
(1//6, √(1//2))

This evaluates at q = 1 with the direct rational/radical evaluator. It does not retain a symbolic parameter or construct a level field.

Use the same reciprocal variable for level values and generic formulas:

using QRecoupling

v = q6j(Exact(10), 1, 1, 1, 1, 1, 1)
# (2√3 − 3)/3 = (2x² − 7)/3, where x = 2cos(π/12)

s = q6j(Symbolic(), 1, 1, 1, 1, 1, 1)
g = x_form(s)       # (x² − 3)/(x⁴ − 3x² + 2), where x = q + q⁻¹
Exact expression in x = q + q⁻¹
  = (x² − 3) / (x⁴ − 3x² + 2)

Exact(k) performs arithmetic at a specified level. Symbolic() retains only a factorial rule, without evaluating the sum during construction or display. It displays a finite sum using F(n) = ∏[r=1:n] U_{r-1}(x/2), the symmetric q-factorial written entirely in x. This avoids an expensive polynomial expansion just because a value was printed.

large = q6j(Symbolic(), 8, 8, 8, 8, 8, 8)
# A finite factorial sum in x; no large expansion on display.

expanded = x_form(large)   # explicitly pay for the full generic sum
again = x_form(large)      # reuse the cached calculation; return an owned copy

Dimensions and theta graphs use the same symbolic x-form interface. Radicals are displayed as products of ψ factors, where ψe(x) is the minimal polynomial of 2cos(2π/e). `phiform(value)requests cyclotomic factorization explicitly. Braiding and twist phases preserve their branch throughu² − x·u + 1 = 0, withu = q`: x alone cannot distinguish q from its reciprocal.

Generic square roots specify an algebraic expression. To evaluate with the package's numerical branch convention, use qeval(s; q=...) or a level target on the original symbolic value. A polynomial identity does not license arbitrary changes of square-root branches at complex q.

Stored x-form and explicit radicals

Exact(k) displays the stored P(x)√R(x) form. Printing does not run radical extraction or compute a numerical approximation. Large polynomials are summarized by size; their exact coefficients remain available.

v.x_value               # named pair (P, R) of stored polynomials
QRecoupling.xpolynomial(v)          # P coefficients, in ascending powers of x
QRecoupling.radicand(v)             # R coefficients, in ascending powers of x
radical(v)              # explicitly request a nested-square-root expression
(2√3 − 3)/3

v.rad is shorthand for radical(v). It is a computation, not a stored display field. Treat the polynomials exposed by v.x_value as read-only; copy them before mutation. For sums, s.x_value gives the corresponding collection of polynomial pairs.

no_surd = radical(q6j(Exact(5),1,1,1,1,1,1))
no_surd
no radical form: v² has degree 3 over ℚ at level 5, and only a power of two is a tower of square roots
  (`v.x_value` gives (P, R), which is then the value's only closed form)
  ≈ 0.08814600002041936

radical returns a RadExpr when it succeeds, otherwise a NoRadical with a reason:

kindMeaning
:noneNo expression in real nested square roots exists
:untriedThe degree budget prevented the search
:longThe expression exceeds an explicitly requested length budget
:failedThe descent could not finish its required sign checks

Here “radical” means nested square roots, not arbitrary nth roots. A :none result is not a claim that no representation using more general radicals exists. The default degree_limit is 32; maxlen=0 imposes no expression-length cap. Increasing the degree limit can be very expensive in both time and memory.

To request an approximate value alongside the stored x-form:

show(IOContext(stdout, :approximate=>true), MIME"text/plain"(), v)
Exact value at level k = 10   (x = 2cos(π/12), degree 4 over ℚ)
  = (2x² − 7)/3
  ≈ 0.15470053837925152

Approximation is now opt-in at every degree. The old display controls :radicals and :radical_degree_limit no longer select radical extraction; call radical explicitly.

Numerical conversion

Float64(v)
setprecision(BigFloat, 512) do
    BigFloat(v)
end
numeric_value(v; bits=128)
0.1547005383792515290182975610039149112949

Single ExactX values use adaptive guard precision for their selected real embedding and round to the requested output precision. The polynomial cancellation estimate is not an interval certificate. Negative radicands are rejected rather than silently changed to zero. General factorial rules can produce complex values even at a level; a real x-value cannot silently replace the required phase.

The numerical evaluation of a sum of several exact radical classes has a separate cancellation problem; the single-value precision guarantee above should not be assumed for arbitrary ExactXSum expressions. Equality and iszero of a sum are still decided exactly, never by a numerical tolerance; see Checking and proving identities.

API reference

QRecoupling.SymbolicValue — Type
SymbolicValue

A recoupling symbol as an exact expression in q, with no level and no evaluation: what Symbolic() returns. It retains the factorial rule without evaluating the sum.

Displayed as the rule, a finite factorial sum in x = q + q⁻¹, whatever the labels are. Printing carries out no sum or polynomial factorization. Its output length is bounded; the cost of formatting factorial and ψ factors still depends on the rule.

Expanding is a separate request, and which of the two you want depends on the basis:

cost at j = 6
x_form(v)the sum carried out in x: √(∏ψ)·P(x)/Q(x)0.3 ms
phi_form(v)the sum carried out in q and factored over the cyclotomics10 ms

phi_form is thirty to fifty times the cost of x_form on the same symbol, and essentially all of the difference is one call to factor over ℤ[q]; see its docstring. x_form is cached on the value, phi_form is not.

Exact(k) specialises the same expression to a level.

source
QRecoupling.x_form — Function
x_form(v::SymbolicValue) -> XValue

The exact value for generic q as √(∏_{e ∈ rad} ψ_e(x)) · num(x)/den(x), x = q + q⁻¹.

Named for its pair: x_form(v) carries the sum out in x, phi_form carries it out in q and factors it, which costs 30–50× as much (0.26 ms against 10.1 ms on {6 6 6; 6 6 6}). Use x_form unless the cyclotomic factors are what you are after.

It was called xvalue, one letter from ExactX's v.x_value and meaning something else — that one is the stored (P, R) of a value at a level, this one is an expansion of a generic-q rule. The old name still works and warns; it goes in later version.

Expansion is explicit and may still be expensive at large labels; nothing about displaying v triggers it. The result is cached on the symbolic value, so asking twice is free; callers receive an owned copy, so mutating its polynomial coefficients cannot corrupt later requests or display. Square roots describe the formal algebraic expression; numerical evaluation uses the rule's branch convention.

source
QRecoupling.XValue — Type
XValue

The exact value √(∏_{e ∈ rad} ψ_e(x)) · num/den with num, den ∈ ℤ[x], built by xvalue(rad, num, den), which normalises (sorted distinct rad, gcd(num, den) = 1, primitive with positive leading coefficient). Normalised values are equal exactly when the structs are, so == decides equality. The square-root branch is fixed by the construction; prove_identity compares like with like.

source
QRecoupling.ExactX — Type
ExactX

An exact value at level k, written in the real variable x = q + q⁻¹ = 2cos(π/h), h = k + 2:

v = P(x) · √(R(x)),     P, R ∈ ℚ[x] reduced modulo Ψ_h.

sqclass lists the ψ indices under the root (empty when v = P(x)); a class that is a rational square at this level is folded into P. Degrees are below φ(2h)/2.

Printing shows the stored P(x)·√(R(x)). radical gives nested square roots on request, or says why there are none. Properties:

v.x_valuethe pair (P, R), the value being P(x)·√(R(x))
v.radshorthand for radical(v)

Lower-level accessors (not exported): radical_form, xpolynomial, radicand, has_radical_form.

source
QRecoupling.ExactXSum — Type
ExactXSum

A sum Σ_S c_S(x)·√(∏_{e ∈ S} ψ_e(x)) at one level, keyed by the square class S. Products fuse classes and stay exact; sums cannot, so they are kept apart.

Two keys may denote the same root at a level, so only an empty sum is immediately zero; iszero uses the structural and norm tests, then exact algebraic numbers.

source
QRecoupling.radical — Function
radical(v::ExactX; maxlen = 0, degree_limit = 32) -> RadExpr or NoRadical

The value in real nested square roots, e.g. (√5 − 3)/2, or a NoRadical saying why there is none. No length limit by default (maxlen > 0 adds one); the descent is exponential in the value's degree, so past degree_limit it is not attempted and the number is given instead.

radical(q6j(Exact(3), 1, 1, 1, 1, 1, 1))    # (√5 − 3)/2, the Fibonacci level
radical(q6j(Exact(5), 1, 1, 1, 1, 1, 1))    # no radical form: v² has degree 3 over ℚ at level 5, …
radical(q6j(Exact(254), 45, 45, 45, 30, 30, 30))          # degree 64: not attempted, with the number
radical(q6j(Exact(254), 45, 45, 45, 30, 30, 30); degree_limit = 64)   # …and this is the long wait
source
radical(s::ExactXSum; maxlen = 0, degree_limit = 32) -> RadExpr or NoRadical

Term by term, added up; the first term without a radical form is the answer, with the sum's number.

source
QRecoupling.NoRadical — Type
NoRadical

What radical returns when there is no nested-radical expression, with the reason in kind:

  • :none — none exists (the degree of v² is not a power of two).
  • :untried — the field degree is past degree_limit; not decided.
  • :long — one exists but exceeds maxlen (only with maxlen > 0).
  • :failed — the descent could not certify a sign within DESCENT_MAX_BITS.

Printing it prints the reason. float is deliberately not defined: there is no number here.

source
QRecoupling.numeric_value — Function
numeric_value(v::ExactX; bits=precision(BigFloat)) -> BigFloat or nothing

Evaluate the selected real embedding with adaptive guard precision, then round to bits bits. Returns nothing if the working-precision budget is exhausted. The Horner mass is an error estimate, not an outward-rounded interval certificate. Negative radicands are rejected rather than clamped to zero.

source
numeric_value(s::ExactXSum) -> BigFloat or nothing

The number the sum denotes, each term certified as in numeric_value(::ExactX).

source