httk.core.vectors.surdvector¶
Exact square-root arithmetic: SurdVector and SurdScalar.
Where FracVector closes the rationals under +,-,*,/,
SurdVector closes the rationals and the square roots of arbitrary positive rationals
under those same operations. It is the exact-arithmetic model for Cartesian crystallographic
geometry, where FracVector alone is not enough.
Why a square-root extension, and where¶
Symmetry operations and Wyckoff coordinates live in fractional coordinates and are already
exactly closed over FracVector: point-group operations are integer matrices and
translations are rationals, so no radicals ever appear. Radicals appear only when one moves to a
Cartesian frame: the hexagonal/trigonal basis carries a \(\sqrt 3\), Cartesian rotation
matrices carry \(\sqrt 2\)/\(\sqrt 3\), and a bond length is \(\sqrt{\text{rational}}\)
under a rational metric. SurdVector is exactly the arithmetic closure needed there.
The purpose boundary (magnitudes vs. linear structure)¶
For pure magnitude questions — comparing, sorting or testing equality of distances — the
squared representation is exact, cheaper, and already fully supported by FracVector: a
squared length lengthsqr is rational and a metric/Gram matrix G = B*B^T is rational, so
“is bond A shorter than bond B?” is an exact rational comparison of lengthsqr values. That
remains the recommended fast path.
Squaring fails, however, as soon as radicals sit inside additive / linear structure, and that
is exactly where SurdVector earns its keep:
components are signed and additive, and \((a+b)^2 \neq a^2 + b^2\) — the cross term is itself a radical;
a metric determines a basis only up to an orthogonal transformation, so squaring throws away orientation and chirality;
even scalar sums are not closed under squaring: \((\sqrt2+\sqrt3)^2 = 5 + 2\sqrt6\).
So SurdVector composes with the squared strategy rather than replacing it: its canonical
form collapses to the plain rational whenever a value happens to be rational, so a computation that
lands back in the rationals is transparently rational again.
The mathematical model¶
The values form the field \(\mathbb{Q}[\sqrt n : n \text{ squarefree}]\) — finite sums
\(\sum_r q_r\sqrt r\) with rational coefficients \(q_r\), the rationals being the radicand
\(r = 1\) term. The canonical form is the map {squarefree radicand -> rational coefficient}
with no zero entries; it is unique because the \(\sqrt r\) (over squarefree \(r\)) are
linearly independent over \(\mathbb{Q}\). Consequences used throughout:
Equality and zero-detection are exact: equal values have equal coefficient maps.
Products combine radicands: \(\sqrt r\,\sqrt s = c\,\sqrt t\) where \(r s = c^2 t\) with \(t\) squarefree (see
square_part).Division is a true field inverse via iterated per-prime conjugation: for each prime \(p\) dividing a radicand of the denominator, multiply numerator and denominator by the conjugate that flips the sign of every \(\sqrt r\) with \(p \mid r\); each such step eliminates \(p\). The coefficient count can grow like \(2^k\) in the number \(k\) of distinct primes among the denominator’s radicands — tiny in practice (crystallographic denominators involve one or two radicands).
Sign is exactly decidable: refine rational lower/upper bounds on each \(\sqrt r\) (via
integer_sqrt()at increasing precision) until the summed interval excludes zero. This terminates because a nonzero surd is never zero.Square roots are closed over positive rationals but NOT over surds (no nested radicals):
sqrt_of()takes any nonnegative rational to an exact surd, but there is no exact \(\sqrt{1+\sqrt2}\). Hencelength()is exact precisely whenlengthsqris rational — which canonical arithmetic guarantees whenever the geometry is metric-rational (the crystallographic case); otherwise it raises.
Classes¶
An immutable exact tensor over the squarefree-radical field |
|
A scalar |
Module Contents¶
- class httk.core.vectors.surdvector.SurdVector(value, dim=None)¶
Bases:
httk.core.vectors.vector_backend.VectorBackendAn immutable exact tensor over the squarefree-radical field \(\mathbb{Q}[\sqrt n : n\ \text{squarefree}]\).
A SurdVector is a map
{squarefree radicand -> FracVector coefficient}(all coefficients sharing onedim); radicand1is the rational part. It is stored canonically — coefficients simplified, all-zero coefficients dropped — so the representation is unique and equality/zero-detection are exact. LikeFracVectorit is immutable and hashable.See the module docstring for the field facts, the fractional-vs-Cartesian motivation, and the magnitude-vs-linear-structure purpose boundary.
- Parameters:
value (Any) – An existing SurdVector or SurdScalar, returned unchanged; a rational scalar/nested sequence accepted by
FracVector(which becomes the radicand-1 component); or a{squarefree radicand -> FracVector coefficient}mapping in canonical component form (as emitted byrepr()). Non-squarefree radicands are not folded here — usefrom_radicand_map()for that.dim (tuple[int, Ellipsis] | None) – The shared coefficient shape, used only with the mapping form; inferred from the coefficients when omitted, and required to pin the shape of an all-zero (empty) mapping.
- classmethod from_radicand_map(mapping)¶
Compose a SurdVector from a
{radicand -> coefficient}mapping.Radicands are positive integers and need not be squarefree — each is normalized via
square_part(sqrt(radicand) = s*sqrt(r)) and the coefficients (FracVector-like, all of one shape) folded together canonically.
- classmethod sqrt_of(q)¶
Return the exact square root of a nonnegative rational
qas aSurdScalar.The result is a plain rational when
qis a perfect square (e.g.sqrt_of(4/9) == 2/3) and otherwise a single-radical surd (sqrt_of(8) == 2*sqrt(2)).sqrt(p/q)is normalized assqrt(p*q)/qso the stored radicand is always a positive squarefree integer (sqrt_of(1/2) == sqrt(2)/2). RaisesValueErroron a negative argument — there is no exact square root of a surd (no nested radicals), only of a rational.- Parameters:
q (Any) – A nonnegative rational value.
- Returns:
Its exact square root.
- Return type:
- classmethod zero(dim=())¶
The zero SurdVector of shape
dim(aSurdScalarfor the default()).- Parameters:
- Returns:
The zero SurdVector or SurdScalar.
- Return type:
- classmethod one()¶
The scalar
1.
- property is_rational: bool¶
True iff the value is purely rational (only the radicand-1 term is present).
- is_zero()¶
True iff the value is exactly zero (empty canonical form).
- property radicands: tuple[int, Ellipsis]¶
The sorted squarefree radicands present in the canonical form.
- coefficient(radicand)¶
Return the FracVector coefficient of
sqrt(radicand)(a zero tensor when absent).- Parameters:
radicand (int) – The radicand whose coefficient to retrieve.
- Returns:
The coefficient, or a zero tensor when absent.
- Return type:
- T()¶
Return the transpose, transposing each radicand’s coefficient tensor.
- Returns:
The transposed tensor.
- Return type:
- dot(other)¶
Return the vector dot product of two 1-D SurdVectors (
sum a_i b_i).- Parameters:
other (Any) – The other 1-D SurdVector.
- Returns:
The exact scalar dot product.
- Return type:
- lengthsqr()¶
Return the squared length
A * A^Tas aSurdScalar.- Returns:
The exact squared length.
- Return type:
- length()¶
Return the exact length
sqrt(lengthsqr)as aSurdScalar.Exact precisely when
lengthsqris rational — which canonical arithmetic guarantees for a difference of Cartesian sites under a rational metric (the crystallographic case). Whenlengthsqris itself irrational the length would be a nested radical (sqrt(a + b*sqrt(c))), which is outside the field, so this raisesValueError.- Returns:
The exact length when the squared length is rational.
- Return type:
- det()¶
Return the determinant of a 3x3 SurdVector as a
SurdScalar.- Returns:
The exact determinant.
- Return type:
- inv()¶
Return the inverse of a 3x3 SurdVector via the adjugate and the scalar field inverse.
- Returns:
The exact inverse matrix.
- Return type:
- to_fractions_approx(prec=fractions.Fraction(1, 10**30))¶
A deterministic nested list of
fractions.Fractionwithinprecof the true value.Exact (not merely within
prec) whenever the value is rational. This is thecompute(prec)-shaped rational approximation reused by the Decimal rendering.- Parameters:
prec (fractions.Fraction) – The maximum elementwise approximation error.
- Returns:
Nested rational approximations of the values.
- Return type:
Any
- to_floats(prec=fractions.Fraction(1, 10**30))¶
Return a nested list of floats via a high-precision exact rational approximation.
- Parameters:
prec (fractions.Fraction) – The maximum elementwise approximation error.
- Returns:
Nested floating-point approximations of the values.
- Return type:
Any
- property fractions: httk.core.vectors.vector_api.Fractions¶
Return the exact or deterministic rational hub representation.
- class httk.core.vectors.surdvector.SurdScalar(value, dim=None)¶
Bases:
SurdVectorA scalar
SurdVector(shape()): a single field element \(\sum_r q_r\sqrt r\).Adds the scalar-only operations — the field inverse, exact sign and ordering, and Decimal rendering — that need a single value rather than a tensor.
- Parameters:
value (Any) – A rational scalar/nested sequence accepted by
FracVector, or an existing SurdVector or SurdScalar, returned unchanged.
- inverse()¶
Return the multiplicative inverse
1/self(raisesZeroDivisionErroron zero).- Returns:
The exact multiplicative inverse.
- Return type:
- sign()¶
Return the exact sign of the value:
-1,0or1.For an irrational value the sign is decided by refining rational lower/upper bounds on each
sqrt(r)(frominteger_sqrt()at increasing precision) and summing the weighted intervals until the total interval excludes zero — which always happens in finitely many steps because a nonzero surd is bounded away from zero.- Returns:
-1,0, or1according to the exact sign.- Return type:
- classmethod cos_degrees(q)¶
Return
cos(q degrees)as an exactSurdScalar, or None when it is not a surd.The value lies in the squarefree-radical field precisely when the angle, reduced modulo 360, is a multiple of 15 or of 36 degrees — e.g. \(\cos 30° = \tfrac{\sqrt3}2\), \(\cos 15° = \tfrac{\sqrt6+\sqrt2}4\), \(\cos 36° = \tfrac{1+\sqrt5}4\).
qmay be an int,Fraction, or numeric string (parsed viaany_to_fraction()).That list is complete: \(\cos(2\pi a/b)\) lies in a field generated by square roots of rationals iff the Galois group \((\mathbb{Z}/b)^\times/\{\pm1\}\) of \(\mathbb{Q}(\cos 2\pi/b)\) has exponent at most 2, which holds exactly for \(b \in \{1,2,3,4,5,6,8,10,12,24\}\) — the rational-degree angles that are multiples of 15° or 36°. (Niven’s theorem is the rational-value special case of this classification.) A
Noneresult is therefore a proof that the exact cosine lies outside \(\mathbb{Q}[\sqrt n]\) — usecos()withdegrees=Truefor a deterministic rational approximation in that case.- Parameters:
q (Any) – The angle in degrees.
- Returns:
The exact cosine, or
Noneoutside the surd field.- Return type:
SurdScalar | None
- classmethod sin_degrees(q)¶
Return
sin(q degrees)as an exactSurdScalar, orNonewhen the value lies outside the supported surd field (when90 - qis not in the exact surd-cosine set).Computed as
cos(90 - q)degrees, so exactness follows the same classification ascos_degrees()applied to90 - q: exact when90 - qis a multiple of 15 or 36 degrees, andNoneotherwise (a proof that the exact sine is outside the field). For example,sin(54°)is exact because it iscos(36°).- Parameters:
q (Any) – The angle in degrees.
- Returns:
The exact sine, or
Noneoutside the surd field.- Return type:
SurdScalar | None
- acos_degrees()¶
Return the exact
arccosof this value in degrees over \([0, 180]\), or None.This is the reverse table lookup: the result is an exact rational number of degrees precisely when the value equals the cosine of a multiple of 15° or 36° (the complete set of rational-degree angles with surd cosines — see
cos_degrees()), decided by exact surd equality; otherwise None (the exact angle is then irrational in degrees). RaisesValueError— decided exactly viasign()— when the value lies outside \([-1, 1]\).- Returns:
The exact angle in degrees, or
Nonewhen not represented by the table.- Return type:
fractions.Fraction | None
- to_float(prec=fractions.Fraction(1, 10**30))¶
Return the value as a float via a high-precision exact rational approximation.
- Parameters:
prec (fractions.Fraction) – The maximum approximation error.
- Returns:
The value as a float.
- Return type:
- to_decimal(digits=None, rounding='half_even', max_refinements=None)¶
Render the value as a correctly-rounded
decimal.Decimal.Reuses the exact-math module’s Ziv refinement loop (
_to_decimal): a rational value renders exactly (its finite expansion when it fits, else quantized), and an irrational surd — never on a rational rounding boundary — is rendered by refining the rational approximation until the rounding is determined.digits(significant digits; default: the active decimal context precision),rounding("half_even"/"down") andmax_refinementsmatchsqrt()in Decimal mode.- Parameters:
- Returns:
The correctly rounded decimal value.
- Return type:
Any