Polynomial API
Explicit polynomial representation types backed by FLINT.
UniPoly
Dense univariate polynomial with exact integer or rational coefficients,
backed by FLINT's fmpq_poly_t.
All arithmetic operators are supported: +, -, *, **
(integer exponent), // (exact quotient), % (remainder):
p = UniPoly.from_symbolic(x**2 + pool.integer(-1), x)
q = UniPoly.from_symbolic(x + pool.integer(-1), x)
print(p * q) # x^3 - x^2 - x + 1
print(p // q) # x + 1
print(p % q) # 0Leading (highest-degree) coefficient, as an exact Python int.
0 for the zero polynomial, matching degree == -1 there. Lossless
for coefficients of any size, as coefficients() also now is — so both
are safe on the output of factor_z, resultants and pseudo-division,
where the coefficients grow past 64 bits.
A property, not a method: it is a single FLINT coefficient read.
Convert a symbolic expression to a univariate polynomial in var.
Example:
p = UniPoly.from_symbolic(x**3 + pool.integer(-2) * x + pool.integer(1), x)- ConversionError
If expr contains non-polynomial terms.
Construct a UniPoly from coefficients (constant term first).
Each coefficient may be a Python int or an integer Expr from the
same pool as var.
`` `python p = ExprPool() x = p.symbol("x")
poly = UniPoly.from_coefficients([-1, 0, 1], x)
also accepted:
poly = UniPoly.from_coefficients([p.integer(-1), p.integer(0), p.integer(1)], x) ` ``
Raises TypeError if any coefficient is not an int / integer expression.
Raises OverflowError if any coefficient overflows i64.
Coefficients in ascending degree order, as exact Python ints.
Lossless for coefficients of any size. This used to go through
coefficients_i64(), which does not merely saturate — it returned 0
for anything past i64, so 2**100 * x**2 + 1 came back as
[1, 0, 0]: a quadratic reading as the constant 1, with no exception
and no flag. That is exactly the silent-error class this project gates
against, and it is reachable from ordinary use, since factor_z,
resultants and pseudo-division all grow coefficients past 64 bits.
Factor over ℤ (FLINT; x**n ± 1 and u**n * x**n ± v**n through
cyclotomic polynomials).
factor_list() is in a canonical order that depends only on the
factors: ascending degree, then coefficients compared as signed
integers from the leading term down, then multiplicity. x**12 - 1
gives x - 1, x + 1, x**2 - x + 1, x**2 + 1, x**2 + x + 1, x**4 - x**2 + 1.
MultiPoly
Sparse multivariate polynomial over ℤ. Terms are stored as a map from exponent vectors to coefficients.
+, -, * are supported between MultiPoly objects with
the same variable list.
Build a multivariate polynomial from a symbolic expression.
If vars is omitted, free symbols of expr are used (sorted by internal id for a deterministic order).
Sparse interpolation
Recover a sparse univariate polynomial over F_p from black-box
evaluations using the Ben-Or/Tiwari (Prony-style) algorithm.
evalcallableBlack-box oracle
x ↦ f(x) mod p. Called with a singleintargument and must return anint.term_boundintUpper bound
Ton the number of nonzero terms. Exactly2·Toracle calls are made.primeintField characteristic
p. Must satisfyp > 2·Tandp > max_degree(f).
- list[tuple[int, int]]
List of
(coefficient, exponent)pairs.
- SparseInterpError
On invalid prime, prime too small, or inconsistent oracle.
Example:p = 997 def f(x): return (x100 + 3*x17 + 5) % p terms = sparse_interp_univariate(f, 3, p)
terms ≈ [(1, 100), (3, 17), (5, 0)]
Recover a sparse multivariate polynomial over F_p from black-box
evaluations using Zippel's variable-by-variable algorithm.
evalcallableBlack-box oracle
(x₁, …, xₙ) ↦ f(x₁, …, xₙ) mod p. Called with a Pythonlist[int](one int per variable) and must return anint.varslist[Expr]Symbolic variable expressions in the same order as the coordinates passed to
eval.term_boundintUpper bound
Ton the number of nonzero terms.degree_boundintUpper bound
Don the degree of each individual variable. For the dense fallback, setD ≤ T.primeintField characteristic
p. Must satisfyp > 2·Tandp > D.seedint, optionalPRNG seed for random evaluation points (default 0). Change the seed to recover from occasional Vandermonde singularities.
- MultiPolyFp
Recovered polynomial with coefficients in
[0, p). On 20-variable inputs this is typically ≥ 5× faster in oracle calls than dense interpolation.
- SparseInterpError
On invalid prime, prime too small, or inconsistent oracle.
Example:pool = ExprPool() x, y = pool.symbol("x"), pool.symbol("y") p = 1009 def f(pt): x_, y_ = pt return (x_ * y_ + 3) % p result = sparse_interp(f, [x, y], term_bound=4, degree_bound=3, prime=p)
Compute the primitive GCD of two multivariate polynomials over ℤ using sparse interpolation and the Chinese Remainder Theorem (Zippel method).
Both polynomials must have the same variable list. The result is the
primitive part of gcd(f, g) with positive leading coefficient.
f, gMultiPolyInput polynomials with the same variable list.
term_boundintUpper bound on the number of nonzero terms in the GCD.
degree_boundintUpper bound on the per-variable degree of the GCD in
x₂,…,xₙ. The degree inx₁is probed automatically.seedint, optionalPRNG seed. Change on failure (default 0).
- MultiPoly
The primitive GCD with positive leading coefficient.
- SparseGcdError
If the interpolation fails or the variable lists are incompatible.
pool = ExprPool()
x, y = pool.symbol("x"), pool.symbol("y")
f = MultiPoly.from_symbolic((x - pool.integer(1)) * (x + y), [x, y])
g = MultiPoly.from_symbolic((x + pool.integer(1)) * (x + y), [x, y])
h = gcd_sparse(f, g, term_bound=4, degree_bound=3)
# h represents x + yRationalFunction
Construct a RationalFunction from two UniPoly numerator / denominator.
`python p = ExprPool() x = p.symbol("x") numer = UniPoly.from_coefficients([p.integer(-1), p.integer(0), p.integer(1)], x) # x²-1 denom = UniPoly.from_coefficients([p.integer(-1), p.integer(1)], x) # x-1 rf = RationalFunction(numer, denom) # → (x+1) after GCD reduction `
Raises ValueError if denom is the zero polynomial.
Construct a rational function and normalize by GCD.
Example:
# (x^2 - 1) / (x - 1) → normalized to x + 1
rf = RationalFunction.from_symbolic(
x**2 + pool.integer(-1),
x + pool.integer(-1),
[x]
)
print(rf) # x + 1Polynomial utilities
Convert a polynomial expression to Horner form.
expr must be a univariate polynomial in var.
Returns a new Expr in Horner form a₀ + x*(a₁ + x*(…)).
Roots, resultants and factorisation
Isolate all real roots of a polynomial expression.
Returns a list of RootInterval objects sorted by lower endpoint.
Each interval contains exactly one real root of the squarefree part of
poly. Repeated roots appear once each.
polyExprA univariate polynomial expression with integer coefficients.
varExprThe polynomial variable.
- list[RootInterval]
- RealRootError
If
polyis not a polynomial with integer coefficients, or is the zero polynomial.Example:pool = ExprPool() x = pool.symbol("x") roots = real_roots(x**2 - pool.integer(4), x)
Refine a RootInterval to at least prec bits of precision.
Uses bisection with floating-point Horner evaluation. For exact rational
roots (lo == hi), returns a zero-radius ball.
polyExprThe same polynomial passed to
real_roots().intervalRootIntervalOne element of the list returned by
real_roots().varExprThe polynomial variable.
precintDesired precision in bits (minimum 53, clamped to
max(53, prec)).
- ArbBall
Rigorous floating-point ball containing the root.
Example:pool = ExprPool() x = pool.symbol("x") ivs = real_roots(x2 - pool.integer(2), x) ball = refine_root(x2 - pool.integer(2), ivs[1], x, 53)
A closed rational interval [lo, hi] isolating exactly one real root.
For an exact rational root r, lo == hi == r.
Compute the resultant of two polynomial expressions with respect to a variable.
Both p and q must be polynomial expressions with integer
coefficients. The returned expression is:
- An integer constant in the univariate case (only
varappears). - A polynomial in the remaining variables in the multivariate case
(
varhas been eliminated).
The returned DerivedResult carries a "Resultant" derivation
step tagged with the Lean 4 theorem
Polynomial.resultant_eq_zero_iff_common_root.
Raises ResultantError if either input is not a polynomial with
integer coefficients.
Example:
pool = ExprPool()
x = pool.symbol("x")
y = pool.symbol("y")
p = x**2 + y**2 - pool.integer(1)
q = y - x
r = resultant(p, q, y)
# r.value == 2*x^2 - 1Compute the subresultant polynomial remainder sequence of two univariate polynomials with integer coefficients.
Returns a Python list of Expr objects ordered
[p, q, S₂, S₃, …, Sₖ].
Both polynomials must be univariate in var with integer
coefficients. Multivariate inputs raise ResultantError.
Example:
pool = ExprPool()
x = pool.symbol("x")
p = x**2 - pool.integer(1)
q = x - pool.integer(1)
prs = subresultant_prs(p, q, x)
# prs == [p, q, last_element]Symbolic / parametric Routh–Hurwitz stability analysis.
Given a characteristic polynomial poly in the analysis variable var
whose coefficients may be symbolic expressions in free parameters, returns a
dict with:
"degree": the degree ofpolyinvar;"first_column": the Routh-array first-column entries (top to bottom) asExprobjects in the parameters;"condition": the stability condition as a single predicateExpr— a conjunction ofentry > 0over the non-trivial first-column entries.
For example s**2 + a*s + b yields the condition a > 0 ∧ b > 0 and
s**3 + a*s**2 + b*s + c yields a > 0 ∧ a*b - c > 0 ∧ c > 0.
Factor a dense univariate polynomial over from ascending
integer coefficients (reduced mod p).
p must be prime (E-POLY-009 otherwise) and the polynomial non-zero
mod p (E-POLY-008). The factors are monic; .unit is the leading
coefficient that restores the input. factor_list() is sorted by
ascending degree, then by the residues from the leading term down.
Modular arithmetic
binomial(a, b) mod p**k, exactly, for p prime.
Uses the Andrew Granville / Davis–Webb factorisation of n! into its
p-free part, which at k = 1 is Lucas' theorem exactly. The cost is
O(p·k³ + log_p(a)·p·k) and does not grow with a beyond the
logarithm, so a far larger than p is the ordinary case rather than
the hard one.
b < 0 and b > a are not errors: the binomial coefficient is 0.
>>> import alkahest as ak
>>> ak.binomial_mod(2 * 11 - 1, 10, 11, 3) # Wolstenholme
1
>>> ak.binomial_mod(1_000_000, 3, 7, 4)
2261
>>> ak.binomial_mod(5, 9, 7, 4)
0- HolonomicError
E-HOLO-006whenpis not prime,k < 1, orp**k >= 2**62;E-HOLO-008when the work budget would be exceeded.