Latest Results
fix(integrate): recognize ℚ(√m,√n) two-radical constants in elliptic output (#178)
Follow-up to #176/#177. Verifying more elliptic integrals showed the
four-real-root quartic with irrational roots (e.g. `∫dx/√(x⁴−5x²+6)`,
P = (x²−2)(x²−3), roots ±√2, ±√3) still printed `2⁵³` floats for the
constants that live in `ℚ(√2, √3)` — `g = 2(√3−√2)` and the `sin²φ`
coefficient `(√2+√3)/2` — because `pretty_const` only handled a *single*
radical (`a + b√n`).
Add form 5: `(a/q)·√m + (b/q)·√n` for distinct squarefree `m < n` over a
common denominator, searched only after the single-radical forms fail.
`∫dx/√(x⁴−5x²+6)` now prints
(2√3−2√2)·EllipticF(asin(√(½(x+√2)(√2+√3)/(√2(x+√3)))), −48+20√6)
with no float reconstructions.
Adds recognizer cases (`2√3−2√2`, `(√2+√3)/2`) and an end-to-end emit test
for the `±√2,±√3` quartic.
Still out of scope (documented, gate-safe — these print exact-float
constants but remain numerically verified): cube-root fields (`∫dx/√(x³−2)`,
real root `2^{1/3}`) and non-symmetric all-complex quartics (`∫dx/√(x⁴+x²+2)`),
whose constants are nested radicals / live in higher-degree fields.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> fix(integrate): recognize ℚ(√m,√n) two-radical constants in elliptic output
Follow-up to #176/#177. Verifying more elliptic integrals showed the
four-real-root quartic with irrational roots (e.g. `∫dx/√(x⁴−5x²+6)`,
P = (x²−2)(x²−3), roots ±√2, ±√3) still printed `2⁵³` floats for the
constants that live in `ℚ(√2, √3)` — `g = 2(√3−√2)` and the `sin²φ`
coefficient `(√2+√3)/2` — because `pretty_const` only handled a *single*
radical (`a + b√n`).
Add form 5: `(a/q)·√m + (b/q)·√n` for distinct squarefree `m < n` over a
common denominator, searched only after the single-radical forms fail.
`∫dx/√(x⁴−5x²+6)` now prints
(2√3−2√2)·EllipticF(asin(√(½(x+√2)(√2+√3)/(√2(x+√3)))), −48+20√6)
with no float reconstructions.
Adds recognizer cases (`2√3−2√2`, `(√2+√3)/2`) and an end-to-end emit test
for the `±√2,±√3` quartic.
Still out of scope (documented, gate-safe — these print exact-float
constants but remain numerically verified): cube-root fields (`∫dx/√(x³−2)`,
real root `2^{1/3}`) and non-symmetric all-complex quartics (`∫dx/√(x⁴+x²+2)`),
whose constants are nested radicals / live in higher-degree fields.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>fix/elliptic-constants-two-radical fix(integrate): exact constants for no-real-root quartic + 12^(-1/4) elliptics (#177)
Follow-up to #176. Verifying a range of elliptic integrals surfaced two
constant classes the first pass still printed as `…/2⁵³` floats:
* The no-real-root quartic (`atan` substitution, e.g. `∫dx/√(x⁴+1)`,
`∫dx/√(x⁴+4)`): the Möbius `L = (px+q)/(rx+s)` coefficients are
`cos/sin θ`-scaled nested-radical floats. Since `atan(L)` is invariant
under scaling `L`'s numerator and denominator by a common constant,
`quartic_no_real` now divides `(p,q,r,s)` by their largest magnitude, so the
shared factor cancels and the coefficients reduce to simple `a+b√n` forms.
`∫dx/√(x⁴+1)` → `(2−√2)·EllipticF(atan((x−1)/((−1+√2)(x+1))), −16+12√2)`.
* `∫dx/√(x³+8)` coefficient `(2√3)^(-1/2) = 12^(-1/4)`: missed because `12`
is not squarefree. The `n^{±1/4}` recognizer now accepts any 4th-power-free
non-square `n` (new `is_quartic_radical`), splitting the old squarefree loop
into a `√n` pass and a `n^{±1/4}` pass.
The exact-constant `atan` denominator can vanish exactly at a sample point
(`x=−1` for `√(x⁴+1)`), a removable singularity of the derivative
representation where `L'/(1+L²)` evaluates to `∞/∞`. The production gate
`verify_higher` already skips non-finite points; the test helper `check_higher`
now does too (it previously passed only by float non-cancellation luck).
Adds recognizer cases (`12^(-1/4)`, `1+√2`) and a regression guard that the
`∫dx/√(x⁴+1)` output carries no float-reconstruction denominators.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> fix(integrate): exact constants for no-real-root quartic + 12^(-1/4) elliptics
Follow-up to #176. Verifying a range of elliptic integrals surfaced two
constant classes the first pass still printed as `…/2⁵³` floats:
* The no-real-root quartic (`atan` substitution, e.g. `∫dx/√(x⁴+1)`,
`∫dx/√(x⁴+4)`): the Möbius `L = (px+q)/(rx+s)` coefficients are
`cos/sin θ`-scaled nested-radical floats. Since `atan(L)` is invariant
under scaling `L`'s numerator and denominator by a common constant,
`quartic_no_real` now divides `(p,q,r,s)` by their largest magnitude, so the
shared factor cancels and the coefficients reduce to simple `a+b√n` forms.
`∫dx/√(x⁴+1)` → `(2−√2)·EllipticF(atan((x−1)/((−1+√2)(x+1))), −16+12√2)`.
* `∫dx/√(x³+8)` coefficient `(2√3)^(-1/2) = 12^(-1/4)`: missed because `12`
is not squarefree. The `n^{±1/4}` recognizer now accepts any 4th-power-free
non-square `n` (new `is_quartic_radical`), splitting the old squarefree loop
into a `√n` pass and a `n^{±1/4}` pass.
The exact-constant `atan` denominator can vanish exactly at a sample point
(`x=−1` for `√(x⁴+1)`), a removable singularity of the derivative
representation where `L'/(1+L²)` evaluates to `∞/∞`. The production gate
`verify_higher` already skips non-finite points; the test helper `check_higher`
now does too (it previously passed only by float non-cancellation luck).
Adds recognizer cases (`12^(-1/4)`, `1+√2`) and a regression guard that the
`∫dx/√(x⁴+1)` output carries no float-reconstruction denominators.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>fix/elliptic-constants-quartic fix(integrate): emit exact algebraic constants in elliptic output (#176)
The genus-1 elliptic reduction computes its constants (`g`, `m`, the
Legendre substitution's root offsets, fitted block coefficients) as `f64`
floats from numerically-found roots, then reconstructed each one with
`rug::Rational::from_f64`. That is exact *for the float* but produces ugly
`…/2⁵³` denominators that merely approximate the true constant — e.g.
`∫dx/√(x³+1)` printed `√3` as `3900231685776981/2251799813685248`,
`3^(-1/4)` and `(2+√3)/4` likewise.
Add `pretty_const`: before falling back to the float reconstruction,
recognize `v` as a simple algebraic number — rational `p/q`, `(p/q)·√n`,
`(p/q)·n^{±1/4}`, or `a+b√n` — via continued-fraction convergents at a
tight 1e-11 tolerance, and emit it symbolically. `∫dx/√(x³+1)` now reads
3^(-1/4) · EllipticF(acos((√3 − (x+1))/(x+1+√3)), 1/2 + √3/4)
This is purely a display improvement: the soundness gate still re-verifies
`d/dx F = integrand` numerically, so a mis-recognition can only decline,
never emit a wrong answer. Unrecognized constants keep the exact-float
fallback. Adds round-trip tests for the recognizer and a regression guard
that the headline output carries no 2⁵³-scale denominators.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> fix(integrate): emit exact algebraic constants in elliptic output
The genus-1 elliptic reduction computes its constants (`g`, `m`, the
Legendre substitution's root offsets, fitted block coefficients) as `f64`
floats from numerically-found roots, then reconstructed each one with
`rug::Rational::from_f64`. That is exact *for the float* but produces ugly
`…/2⁵³` denominators that merely approximate the true constant — e.g.
`∫dx/√(x³+1)` printed `√3` as `3900231685776981/2251799813685248`,
`3^(-1/4)` and `(2+√3)/4` likewise.
Add `pretty_const`: before falling back to the float reconstruction,
recognize `v` as a simple algebraic number — rational `p/q`, `(p/q)·√n`,
`(p/q)·n^{±1/4}`, or `a+b√n` — via continued-fraction convergents at a
tight 1e-11 tolerance, and emit it symbolically. `∫dx/√(x³+1)` now reads
3^(-1/4) · EllipticF(acos((√3 − (x+1))/(x+1+√3)), 1/2 + √3/4)
This is purely a display improvement: the soundness gate still re-verifies
`d/dx F = integrand` numerically, so a mis-recognition can only decline,
never emit a wrong answer. Unrecognized constants keep the exact-float
fallback. Adds round-trip tests for the recognizer and a regression guard
that the headline output carries no 2⁵³-scale denominators.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>fix/elliptic-symbolic-constants feat(simplify): coefficient-aware Pythagorean + angle identities in simplify_trig (#175)
* feat(simplify): coefficient-aware Pythagorean + angle identities in simplify_trig
Strengthen the `trig_rules` ruleset used by `simplify_trig`:
- Coefficient-aware Pythagorean: `a·sin²(u) + a·cos²(u) → a` for any common
factor `a` (numeric or symbolic, `u` any subexpression), including when
embedded in a larger sum — leftover numeric terms are folded so
`3 + 2·sin²(u) + 2·cos²(u) → 5`. The bare `sin²+cos²→1` case still works.
- Double-angle: `2·sin(u)·cos(u) → sin(2u)`, `cos²(u) − sin²(u) → cos(2u)`.
- Angle-subtraction: `sin(a)cos(b) − cos(a)sin(b) → sin(a−b)`,
`cos(a)cos(b) + sin(a)sin(b) → cos(a−b)`. These collapse a 2-link planar
Jacobian determinant `l1·l2·[cos θ1 sin(θ1+θ2) − sin θ1 cos(θ1+θ2)]` to
`l1·l2·sin(θ2)` (after the default simplifier's like-term collection).
The trig set now leads with the structural FlattenMul/FlattenAdd normalizers so
the AC-sensitive identity rules see flattened nodes even when input arrives as
nested binary trees (as from the Python surface). Both normalizers only
restructure nested Add/Mul; no arithmetic, no regressions. No public signature
changes; rules are gated to `trig_rules` only and never touch the default set.
Full `cargo test -p alkahest-cas --lib` stays green (1333 passed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(ci): avoid clippy::cmp_owned in trig coefficient fold
Use Rational::cmp0() instead of allocating Rational::from(0) for the
zero-comparison flagged by clippy under -D warnings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(simplify): coefficient-aware angle-subtraction in simplify_trig
Generalise the sine/cosine angle-subtraction rules to match a shared
coefficient (numeric or symbolic) around the trig product pair, the same
way the Pythagorean rule already does:
c·sin(a)cos(b) − c·cos(a)sin(b) → c·sin(a−b)
c·cos(a)cos(b) + c·sin(a)sin(b) → c·cos(a−b)
Previously these only fired on the bare (coefficient-free) products, so a
2-link planar Jacobian determinant — which carries a shared l1·l2 factor —
did not collapse. With the coefficient match, the expanded determinant
l1·l2·cos(θ1)·sin(θ1+θ2) − l1·l2·sin(θ1)·cos(θ1+θ2) now reduces to
l1·l2·sin(θ2) under the default simplifier.
A new split_trig_pair helper peels exactly one f(a) and one g(b) factor
off a term, returning the remaining factors as the coefficient multiset;
positive/negative terms must share the same coefficient multiset (sign
tracked via a single -1 factor), so mismatched coefficients such as
2·sin(a)cos(b) − 3·cos(a)sin(b) correctly do NOT collapse.
Adds Rust unit tests for the symbolic-coefficient case and the
mismatched-coefficient guards (numeric and symbolic).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Latest Branches
0%
fix/elliptic-constants-two-radical 0%
fix/elliptic-constants-quartic 0%
fix/elliptic-symbolic-constants © 2026 CodSpeed Technology