zorch.poly.univariate¶
Univariate polynomial evaluation, in evaluation and coefficient form.
powers ¶
powers(x: Array, n: int) -> Array
(1, x, x², …, x^{n-1}) ascending, length n (n static).
Built by log-doubling (powers[m:2m] = powers[:m]·xᵐ) so the traced graph
is O(log n), not n unrolled multiplies — a linear power chain makes the
fused kernel's operand count scale with n and past a few thousand entries
overruns the GPU's kernel-parameter space (the same cliff eval_coeffs
avoids). The monomial-basis evaluation vector: ⟨coeffs, powers(x, n)⟩ =
Σ cᵢ xⁱ.
Source code in zorch/poly/univariate.py
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | |
eval_univariate ¶
eval_univariate(evals: Array, x: Array) -> Array
Evaluate a univariate given by its values on [0, 1, ..., len-1] at
x, by Lagrange interpolation over that integer domain.
A composer over the jitted basis kernel, so itself un-jitted.
Source code in zorch/poly/univariate.py
36 37 38 39 40 41 42 | |
compute_lagrange_basis ¶
compute_lagrange_basis(r: Array, domain: Array) -> Array
All Lagrange basis evaluations L_{D,k}(r) over domain:
L_{D,k}(r) = prod_{j != k} (r - x_j) / (x_k - x_j).
Direct form, not barycentric — barycentric divides by (r - node),
which an r landing on a node would zero.
Source code in zorch/poly/univariate.py
55 56 57 58 59 60 61 62 63 64 65 | |
compute_inv_vandermonde ¶
compute_inv_vandermonde(degree: int, dtype: Any) -> Array
Inverse Vandermonde over the natural domain {0..degree}:
coeffs = M @ evals for evals[j] = p(j).
Built in the base field — the Lagrange basis for an integer domain lives in the prime field — so one matrix serves BF and EF callers; EF evaluations promote at multiply time.
Source code in zorch/poly/univariate.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
eval_coeffs ¶
eval_coeffs(
coeffs: Array,
point: Array,
*,
schedule: str | None = None
) -> Array
p(point) = sum_i coeffs[..., i] * point**i — the coefficient-form
dual of eval_univariate.
schedule: None/"auto" picks the measured per-field optimum;
"horner" forces the unrolled chain (O(n)-deep graph — small n only);
"scan" forces the log-depth prefix product. Byte-identical either way
(exact field arithmetic). Horner fuses through a producer's pending
expression stack; the scan keeps large degrees off both cliffs — a
sequential scan's per-coefficient host launches and an unrolled power
chain's kernel-parameter-space overflow.
Source code in zorch/poly/univariate.py
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | |