Skip to main content

gam_math/
jet_scalar.rs

1//! Order-specific Taylor-jet SCALAR algebras (#932 cutover, doc §A).
2//!
3//! [`crate::jet_tower::Tower4`] carries the full value/gradient/Hessian/`t3`/`t4`
4//! tensor stack: it answers EVERY channel a `gam_models::row_kernel::RowKernel`
5//! consumer can ask for, but at `K = 9` that is a ~50 KiB per-row object whose
6//! by-value copies overflowed the stack and timed out the location-scale fit.
7//! The cutover therefore does NOT instantiate the dense `Tower4<9>` per row; it
8//! carries, per consumer, only the one channel that consumer serves:
9//!
10//! | consumer | channel | scalar here | K=9 size |
11//! |---|---|---|---|
12//! | inner Newton / `row_kernel` | `(v, g, H)` | [`Order2`] | 728 B |
13//! | `row_third_contracted(dir)` | `Σ_c ℓ_{abc} dir_c` | [`OneSeed`] | 1.46 KiB |
14//! | `row_fourth_contracted(u, v)` | `Σ_{cd} ℓ_{abcd} u_c v_d` | [`TwoSeed`] | 2.8 KiB |
15//!
16//! Each is built on [`Order2`] (value/grad/Hessian), which is the production
17//! [`crate::jet_tower::Tower2`] re-expressed behind a generic interface: a row
18//! loss written ONCE against [`JetScalar`] re-instantiates at whatever order /
19//! representation a consumer needs, with the contraction folded INTO the
20//! differentiation (the nilpotent ε / δ directions), so `t3` / `t4` are never
21//! materialised. The single source of truth is the same one expression — the
22//! genus of #736 cross-block drift cannot reappear because there is no separate
23//! channel to forget.
24//!
25//! # Why each scalar is exact (doc §A.1–A.3)
26//!
27//! * [`Order2`] is the order-≤2 truncation of the Leibniz / Faà di Bruno rules.
28//!   Those order-2 terms read ONLY the order-≤2 channels of their inputs (see
29//!   [`crate::jet_tower::Tower4::mul`]: `out.h[i][j]` never touches `t3`/`t4`),
30//!   so its `(v, g, H)` is BIT-IDENTICAL to a full `Tower4<K>` — and identical
31//!   to [`crate::jet_tower::Tower2`], over which it is a thin newtype.
32//! * [`OneSeed`] carries an [`Order2`] base plus one nilpotent ε (`ε² = 0`)
33//!   holding another [`Order2`]. Seeding ε with the fixed direction `u` makes the
34//!   ε-component of the Hessian channel the contracted third `Σ_c ℓ_{abc} u_c`
35//!   (the nilpotent implements `d/dτ|₀` of `ℓ_{ab}(p + τu)` exactly).
36//! * [`TwoSeed`] carries an [`Order2`] base plus ε, δ (`ε² = δ² = 0`, `εδ`
37//!   retained) — four [`Order2`] parts. Seeding ε, δ with `u, v` makes the
38//!   εδ-component of the Hessian channel the contracted fourth
39//!   `Σ_{cd} ℓ_{abcd} u_c v_d` (the single mixed `∂_σ∂_ρ|₀` term, no `σ²`/`ρ²`
40//!   contamination).
41//!
42//! # Stability discipline
43//!
44//! As in [`crate::jet_tower`], humans own primitive stability and the algebra
45//! owns combinatorics: tail-critical special functions enter ONLY as
46//! hand-certified `[f64; 5]` derivative stacks through [`crate::nested_dual::JetField::compose_unary`]
47//! (each scalar consumes the leading entries its order needs), never by
48//! differentiating an unstable primal.
49//!
50//! # Production scalars and the test-only all-channels oracle
51//!
52//! The `JetScalar` trait below is production: it is the bound on the canonical
53//! [`crate::jet_tower::RowProgram::eval`] seam a family row loss is written
54//! against. The order-specific scalars that *consume* it —
55//! [`Order2`] (value/grad/Hessian), [`OneSeed`] (contracted third) and
56//! [`TwoSeed`] (contracted fourth) — are production: the survival location-scale
57//! `RowKernel<9>` builds its joint Hessian / directional derivatives through them
58//! (`survival::location_scale::row_kernel`), paying only the small packed scalar
59//! per row instead of the ~50 KiB dense [`crate::jet_tower::Tower4`].
60//!
61//! The [`crate::jet_tower::Tower4`] all-channels `JetScalar` impl is test-only: it
62//! is the oracle that pins the contracted scalars against the dense
63//! value/grad/Hessian/`t3`/`t4` truth, so it lives in the `#[cfg(test)]` module.
64
65/// Symmetric coefficient operator used by the universal quadratic-form jet
66/// primitive.
67///
68/// Implementations own representation-specific matrix access (diagonal,
69/// dense, low rank, structured, or matrix free). Jet scalars own the one
70/// derivative rule for `x' A x`; callers never spell its chain rule.
71pub trait SymmetricQuadraticCoefficients {
72    /// Input-space dimension of the symmetric operator.
73    fn dimension(&self) -> usize;
74
75    /// Compute `output = A * input` without materializing `A`.
76    fn multiply(&self, input: &[f64], output: &mut [f64]);
77
78    /// One symmetric coefficient `A[row, column]`.
79    fn coefficient(&self, row: usize, column: usize) -> f64;
80
81    /// Visit the operator's input-space upper triangle without materializing a
82    /// dense matrix. The default probes one basis direction at a time through
83    /// [`Self::multiply`], preserving matrix-free operators. Representations
84    /// with direct structure should override this hook so a packed curvature
85    /// lowering is O(K), O(K²), or O(K²R) as appropriate instead of performing
86    /// K full operator applications.
87    ///
88    /// `direction` and `projected` are caller-owned scratch of exact length
89    /// [`Self::dimension`]. Every `(row, column)` with `row <= column` is
90    /// visited exactly once, including structural zeros.
91    fn visit_upper_triangle(
92        &self,
93        direction: &mut [f64],
94        projected: &mut [f64],
95        mut visit: impl FnMut(usize, usize, f64),
96    ) {
97        let dimension = self.dimension();
98        assert_eq!(direction.len(), dimension);
99        assert_eq!(projected.len(), dimension);
100        direction.fill(0.0);
101        for column in 0..dimension {
102            direction[column] = 1.0;
103            self.multiply(direction, projected);
104            direction[column] = 0.0;
105            for row in 0..=column {
106                visit(row, column, projected[row]);
107            }
108        }
109    }
110
111    /// Evaluate the primal quadratic form without materializing the input as a
112    /// separate `f64` vector. Structured operators should override this to
113    /// preserve their representation's natural complexity (for example O(KR)
114    /// for a K-by-R low-rank factor).
115    fn quadratic_value<T, F>(&self, inputs: &[T], value: F) -> f64
116    where
117        F: Fn(&T) -> f64,
118    {
119        assert_eq!(
120            inputs.len(),
121            self.dimension(),
122            "symmetric quadratic-form dimension mismatch"
123        );
124        let mut out = 0.0;
125        for row in 0..inputs.len() {
126            let row_value = value(&inputs[row]);
127            out += self.coefficient(row, row) * row_value * row_value;
128            for column in row + 1..inputs.len() {
129                out += 2.0 * self.coefficient(row, column) * row_value * value(&inputs[column]);
130            }
131        }
132        out
133    }
134}
135
136fn symmetric_quadratic_form_default<T, C>(
137    inputs: &[T],
138    coefficients: &C,
139    constant: impl Fn(f64) -> T,
140    add: impl Fn(&T, &T) -> T,
141    mul: impl Fn(&T, &T) -> T,
142    scale: impl Fn(&T, f64) -> T,
143) -> T
144where
145    C: SymmetricQuadraticCoefficients,
146{
147    assert_eq!(
148        inputs.len(),
149        coefficients.dimension(),
150        "symmetric quadratic-form dimension mismatch"
151    );
152    let mut out = constant(0.0);
153    for row in 0..inputs.len() {
154        let diagonal = mul(&inputs[row], &inputs[row]);
155        out = add(&out, &scale(&diagonal, coefficients.coefficient(row, row)));
156        for column in row + 1..inputs.len() {
157            let cross = mul(&inputs[row], &inputs[column]);
158            out = add(
159                &out,
160                &scale(&cross, 2.0 * coefficients.coefficient(row, column)),
161            );
162        }
163    }
164    out
165}
166
167fn linear_combination_default<T>(
168    inputs: &[T],
169    weights: &[f64],
170    constant: impl Fn(f64) -> T,
171    add: impl Fn(&T, &T) -> T,
172    scale: impl Fn(&T, f64) -> T,
173) -> T {
174    assert_eq!(
175        inputs.len(),
176        weights.len(),
177        "linear-combination dimension mismatch"
178    );
179    inputs
180        .iter()
181        .zip(weights)
182        .fold(constant(0.0), |sum, (input, &weight)| {
183            add(&sum, &scale(input, weight))
184        })
185}
186
187fn multiply_add_default<T>(
188    left: &T,
189    right: &T,
190    addend: &T,
191    mul: impl Fn(&T, &T) -> T,
192    add: impl Fn(&T, &T) -> T,
193) -> T {
194    add(&mul(left, right), addend)
195}
196
197fn composed_sum_default<T>(
198    inputs: &[T],
199    derivative_stacks: &[[f64; 5]],
200    constant: impl Fn(f64) -> T,
201    add: impl Fn(&T, &T) -> T,
202    compose: impl Fn(&T, [f64; 5]) -> T,
203) -> T {
204    assert_eq!(
205        inputs.len(),
206        derivative_stacks.len(),
207        "composed-sum term-count mismatch"
208    );
209    inputs
210        .iter()
211        .zip(derivative_stacks)
212        .fold(constant(0.0), |sum, (input, &stack)| {
213            add(&sum, &compose(input, stack))
214        })
215}
216
217fn affine_compose_default<T>(
218    input: &T,
219    input_scale: f64,
220    input_shift: f64,
221    derivative_stack: [f64; 5],
222    scale: impl Fn(&T, f64) -> T,
223    add_constant: impl Fn(&T, f64) -> T,
224    compose: impl Fn(&T, [f64; 5]) -> T,
225) -> T {
226    compose(
227        &add_constant(&scale(input, input_scale), input_shift),
228        derivative_stack,
229    )
230}
231
232fn affine_composed_sum_default<T>(
233    inputs: &[T],
234    input_scales: &[f64],
235    derivative_stacks: &[[f64; 5]],
236    constant: impl Fn(f64) -> T,
237    add: impl Fn(&T, &T) -> T,
238    scale: impl Fn(&T, f64) -> T,
239    add_constant: impl Fn(&T, f64) -> T,
240    compose: impl Fn(&T, [f64; 5]) -> T,
241) -> T {
242    assert_eq!(inputs.len(), input_scales.len());
243    assert_eq!(inputs.len(), derivative_stacks.len());
244    inputs.iter().zip(input_scales).zip(derivative_stacks).fold(
245        constant(0.0),
246        |sum, ((input, &input_scale), &stack)| {
247            add(
248                &sum,
249                &affine_compose_default(
250                    input,
251                    input_scale,
252                    0.0,
253                    stack,
254                    &scale,
255                    &add_constant,
256                    &compose,
257                ),
258            )
259        },
260    )
261}
262
263fn shared_multiply_add_affine_composed_sum_default<T, const N: usize>(
264    lefts: &[&T; N],
265    right: &T,
266    addend: &T,
267    addend_scales: &[f64; N],
268    input_scales: &[f64; N],
269    derivative_stacks: &[[f64; 5]; N],
270    constant: impl Fn(f64) -> T,
271    add: impl Fn(&T, &T) -> T,
272    mul: impl Fn(&T, &T) -> T,
273    scale: impl Fn(&T, f64) -> T,
274    multiply_add: impl Fn(&T, &T, &T) -> T,
275    affine_compose: impl Fn(&T, f64, f64, [f64; 5]) -> T,
276) -> T {
277    let (representatives, term_sources, source_count) =
278        canonical_shared_source_schedule(|term, representative| {
279            std::ptr::eq(lefts[term], lefts[representative])
280                && addend_scales[term] == addend_scales[representative]
281        });
282    let (value, source_derivatives) =
283        aggregate_shared_source_derivatives(&term_sources, input_scales, derivative_stacks);
284    (0..source_count).fold(constant(value), |sum, source| {
285        let term = representatives[source];
286        let inner = if addend_scales[term] == 0.0 {
287            mul(lefts[term], right)
288        } else if addend_scales[term] == 1.0 {
289            multiply_add(lefts[term], right, addend)
290        } else {
291            multiply_add(lefts[term], right, &scale(addend, addend_scales[term]))
292        };
293        let composed = affine_compose(&inner, 1.0, 0.0, source_derivatives[source]);
294        add(&sum, &composed)
295    })
296}
297
298/// Canonical representatives for repeated inner sources in a shared composed sum.
299///
300/// Backends define source identity at their representation boundary: eager jets
301/// use reference identity, while compiled graphs use node identity. Terms are
302/// equivalent only when their left operand and structural addend coefficient
303/// name the same inner expression. The returned term-to-source map lets every
304/// backend sum outer first/second coefficients before propagating that source.
305#[inline(always)]
306pub(crate) fn canonical_shared_source_schedule<const N: usize>(
307    mut equivalent: impl FnMut(usize, usize) -> bool,
308) -> ([usize; N], [usize; N], usize) {
309    let mut representatives = [0; N];
310    let mut term_sources = [0; N];
311    let mut source_count = 0;
312    for term in 0..N {
313        let mut source = 0;
314        while source < source_count && !equivalent(term, representatives[source]) {
315            source += 1;
316        }
317        if source == source_count {
318            representatives[source] = term;
319            source_count += 1;
320        }
321        term_sources[term] = source;
322    }
323    (representatives, term_sources, source_count)
324}
325
326/// Sum each source's affine-scaled derivative stack through fourth order.
327///
328/// For a term `f(s*x)`, derivative order `r` contributes `f^(r) * s^r`.
329/// The primal value is accumulated once in original term order, while source
330/// stacks deliberately keep a zero value channel so adding their composed jets
331/// cannot perturb that authoritative primal sum.
332#[inline(always)]
333pub(crate) fn aggregate_shared_source_derivatives<const N: usize>(
334    term_sources: &[usize; N],
335    input_scales: &[f64; N],
336    derivative_stacks: &[[f64; 5]; N],
337) -> (f64, [[f64; 5]; N]) {
338    let mut value = 0.0;
339    let mut source_derivatives = [[0.0; 5]; N];
340    for term in 0..N {
341        value += derivative_stacks[term][0];
342        let source = term_sources[term];
343        let mut scale_power = input_scales[term];
344        for order in 1..5 {
345            source_derivatives[source][order] += derivative_stacks[term][order] * scale_power;
346            scale_power *= input_scales[term];
347        }
348    }
349    (value, source_derivatives)
350}
351
352/// A truncated-Taylor scalar carrying derivatives in `K` primaries.
353///
354/// All concrete scalars here ([`Order2`], [`OneSeed`], [`TwoSeed`]) and the full
355/// [`crate::jet_tower::Tower4`] implement the SAME algebra; only the carried
356/// channel set differs. A row loss written once against this interface yields a
357/// different channel set per instantiation, all exact for the channel they serve
358/// (doc §A.0).
359pub trait JetScalar<const K: usize>: crate::nested_dual::JetField + Copy {
360    /// A constant: value `c`, every derivative channel zero.
361    fn constant(c: f64) -> Self;
362
363    /// The seeded variable `p_axis` at value `x`: unit first derivative in slot
364    /// `axis`, all higher channels zero. (The nilpotent / cross channels of the
365    /// directional scalars are seeded zero — callers set ε/δ directions through
366    /// the scalar-specific [`OneSeed::seed_direction`] / [`TwoSeed::seed`].)
367    fn variable(x: f64, axis: usize) -> Self;
368
369    /// Evaluate `inputs' A inputs` from one universal semantic primitive.
370    /// Order-specific scalars may lower the mechanically derived channels
371    /// directly; the default is the exact scalar program over `mul/add/scale`.
372    fn symmetric_quadratic_form<C: SymmetricQuadraticCoefficients>(
373        inputs: &[Self],
374        coefficients: &C,
375    ) -> Self {
376        symmetric_quadratic_form_default(
377            inputs,
378            coefficients,
379            Self::constant,
380            crate::nested_dual::JetField::add,
381            crate::nested_dual::JetField::mul,
382            crate::nested_dual::JetField::scale,
383        )
384    }
385
386    /// Evaluate `sum_i weights[i] * inputs[i]` in one semantic primitive.
387    fn linear_combination(inputs: &[Self], weights: &[f64]) -> Self {
388        linear_combination_default(
389            inputs,
390            weights,
391            Self::constant,
392            crate::nested_dual::JetField::add,
393            crate::nested_dual::JetField::scale,
394        )
395    }
396
397    /// Add a primal constant without changing derivative channels.
398    fn add_constant(&self, constant: f64) -> Self {
399        self.add(&Self::constant(constant))
400    }
401
402    /// Evaluate `self * right + addend` in one semantic primitive.
403    fn multiply_add(&self, right: &Self, addend: &Self) -> Self {
404        multiply_add_default(
405            self,
406            right,
407            addend,
408            crate::nested_dual::JetField::mul,
409            crate::nested_dual::JetField::add,
410        )
411    }
412
413    /// Sum unary compositions directly from certified derivative stacks.
414    fn composed_sum(inputs: &[Self], derivative_stacks: &[[f64; 5]]) -> Self {
415        composed_sum_default(
416            inputs,
417            derivative_stacks,
418            Self::constant,
419            crate::nested_dual::JetField::add,
420            crate::nested_dual::JetField::compose_unary,
421        )
422    }
423
424    /// Exact product as an explicit compiled graph node.
425    fn product(&self, right: &Self) -> Self {
426        self.mul(right)
427    }
428
429    /// Compose a certified outer stack after the affine map
430    /// `u = input_scale * self + input_shift`.
431    fn affine_compose(
432        &self,
433        input_scale: f64,
434        input_shift: f64,
435        derivative_stack: [f64; 5],
436    ) -> Self {
437        affine_compose_default(
438            self,
439            input_scale,
440            input_shift,
441            derivative_stack,
442            crate::nested_dual::JetField::scale,
443            Self::add_constant,
444            crate::nested_dual::JetField::compose_unary,
445        )
446    }
447
448    /// Sum unary compositions whose inputs each carry an affine scale.
449    fn affine_composed_sum(
450        inputs: &[Self],
451        input_scales: &[f64],
452        derivative_stacks: &[[f64; 5]],
453    ) -> Self {
454        affine_composed_sum_default(
455            inputs,
456            input_scales,
457            derivative_stacks,
458            Self::constant,
459            crate::nested_dual::JetField::add,
460            crate::nested_dual::JetField::scale,
461            Self::add_constant,
462            crate::nested_dual::JetField::compose_unary,
463        )
464    }
465
466    /// Evaluate
467    /// `Σ_i f_i(input_scale_i · (left_i · right + addend_scale_i · addend))`
468    /// from the certified derivative stack of each `f_i`. The shared operands
469    /// make expression-level common subexpressions explicit, so optimized
470    /// backends apply their inherited derivative channels once. Expression
471    /// arity is part of the type, and borrowed operands never copy a full tower.
472    /// An exact-zero addend scale (either sign of IEEE zero) removes that addend
473    /// from the corresponding term entirely.
474    fn shared_multiply_add_affine_composed_sum<const N: usize>(
475        lefts: &[&Self; N],
476        right: &Self,
477        addend: &Self,
478        addend_scales: &[f64; N],
479        input_scales: &[f64; N],
480        derivative_stacks: &[[f64; 5]; N],
481    ) -> Self {
482        shared_multiply_add_affine_composed_sum_default(
483            lefts,
484            right,
485            addend,
486            addend_scales,
487            input_scales,
488            derivative_stacks,
489            Self::constant,
490            crate::nested_dual::JetField::add,
491            crate::nested_dual::JetField::mul,
492            crate::nested_dual::JetField::scale,
493            Self::multiply_add,
494            Self::affine_compose,
495        )
496    }
497
498    // The scalar-field algebra — `value`, `add`, `sub`, `mul`, `neg`, `scale`,
499    // and the single Faà di Bruno `compose_unary([f64; 5])` — is declared ONCE on
500    // the shared [`crate::nested_dual::JetField`] supertrait (which the runtime-`p`
501    // flex jets also extend), so it is not re-declared here. The value/composition
502    // convention is identical (`u = self.value()`, the `[f64; 5]` stack shape
503    // [`crate::jet_tower::Tower4`] consumes). The helpers below build on it.
504
505    /// Compose with a unary special-function whose derivative stack is built from
506    /// the scalar base value through `stack_fn`. This evaluates
507    /// `stack_fn(self.value())` once and forwards to
508    /// [`compose_unary`](crate::nested_dual::JetField::compose_unary), so it is bit-identical to the
509    /// explicit `self.compose_unary(stack_fn(self.value()))` form.
510    fn compose_unary_with(&self, stack_fn: impl Fn(f64) -> [f64; 5]) -> Self {
511        self.compose_unary(stack_fn(self.value()))
512    }
513
514    /// `e^self`. Convenience for tame arguments (see module stability note).
515    fn exp(&self) -> Self {
516        let e = self.value().exp();
517        self.compose_unary([e, e, e, e, e])
518    }
519
520    /// `√self`. Caller guarantees positivity.
521    fn sqrt(&self) -> Self {
522        let u = self.value();
523        let s = u.sqrt();
524        self.compose_unary([
525            s,
526            0.5 / s,
527            -0.25 / (u * s),
528            0.375 / (u * u * s),
529            -0.9375 / (u * u * u * s),
530        ])
531    }
532
533    /// `ln(self)`. Caller guarantees positivity. Same derivative stack
534    /// [`crate::jet_tower::Tower4::ln`] uses, so any program written over both
535    /// matches term-for-term.
536    fn ln(&self) -> Self {
537        let u = self.value();
538        let r = 1.0 / u;
539        self.compose_unary([u.ln(), r, -r * r, 2.0 * r * r * r, -6.0 * r * r * r * r])
540    }
541
542    /// `1/self`.
543    fn recip(&self) -> Self {
544        let r = 1.0 / self.value();
545        let r2 = r * r;
546        self.compose_unary([r, -r2, 2.0 * r2 * r, -6.0 * r2 * r2, 24.0 * r2 * r2 * r])
547    }
548
549    /// `self^a` for real exponent `a`. Caller guarantees a positive base.
550    /// Mirrors [`crate::jet_tower::Tower4::powf`] (falling-factorial stack).
551    fn powf(&self, a: f64) -> Self {
552        let u = self.value();
553        self.compose_unary([
554            u.powf(a),
555            a * u.powf(a - 1.0),
556            a * (a - 1.0) * u.powf(a - 2.0),
557            a * (a - 1.0) * (a - 2.0) * u.powf(a - 3.0),
558            a * (a - 1.0) * (a - 2.0) * (a - 3.0) * u.powf(a - 4.0),
559        ])
560    }
561
562    /// `ln Γ(self)`. Caller guarantees a positive argument. Uses the SAME
563    /// hand-certified derivative stack [`crate::jet_tower::Tower4::ln_gamma`]
564    /// consumes ([`crate::jet_tower::ln_gamma_derivative_stack`]), so any
565    /// program written over both matches term-for-term.
566    fn ln_gamma(&self) -> Self {
567        self.compose_unary(crate::jet_tower::ln_gamma_derivative_stack(self.value()))
568    }
569
570    /// `ψ(self) = d/dx ln Γ(x)` (digamma). Caller guarantees a positive
571    /// argument. Same hand-certified stack
572    /// [`crate::jet_tower::digamma_derivative_stack`].
573    fn digamma(&self) -> Self {
574        self.compose_unary(crate::jet_tower::digamma_derivative_stack(self.value()))
575    }
576}
577
578/// Lift a fixed-width primary jet through an independent second-order
579/// direction. Primary variables live in the inner scalar; constructing one
580/// must not seed the outer direction, which is reserved for a family or
581/// hyperparameter derivative selected by the caller.
582impl<S, const K: usize> JetScalar<K> for crate::nested_dual::Dual2<S>
583where
584    S: JetScalar<K>,
585{
586    #[inline]
587    fn constant(c: f64) -> Self {
588        Self {
589            v: S::constant(c),
590            g: S::constant(0.0),
591            h: S::constant(0.0),
592        }
593    }
594
595    #[inline]
596    fn variable(x: f64, axis: usize) -> Self {
597        Self {
598            v: S::variable(x, axis),
599            g: S::constant(0.0),
600            h: S::constant(0.0),
601        }
602    }
603}
604
605/// A Taylor-jet scalar whose primary dimension is selected at runtime.
606///
607/// This is the dimensioned counterpart of [`JetScalar`].  Its algebra is the
608/// same; only the constructors receive the row's actual primary count.  The
609/// fixed-size scalar implementations below bridge to this trait as well, which
610/// lets one row program serve both the const-generic derivative oracles and the
611/// runtime-sized production backends without duplicating the expression.
612pub trait RuntimeJetScalar<'arena>: Clone {
613    /// Storage arena used by runtime-backed scalars. Fixed derivative oracles
614    /// use the unit type because their storage is inline.
615    type Workspace: ?Sized;
616
617    /// A constant in a `dimension`-primary algebra.
618    fn constant(c: f64, dimension: usize, workspace: &'arena Self::Workspace) -> Self;
619    /// A seeded variable in a `dimension`-primary algebra.
620    fn variable(x: f64, axis: usize, dimension: usize, workspace: &'arena Self::Workspace) -> Self;
621    /// A constant in the same runtime algebra as `self`.
622    ///
623    /// Implementations must construct the constant directly, with every
624    /// derivative channel exactly zero. A shaped constant is an algebra
625    /// primitive: synthesizing one through [`Self::compose_unary`] needlessly
626    /// traverses every live derivative channel.
627    fn constant_like(&self, c: f64) -> Self;
628    /// Replace only the primal value, preserving every derivative channel.
629    fn with_value(&self, value: f64) -> Self;
630
631    /// Evaluate `inputs' A inputs` from the same universal semantic primitive
632    /// as [`JetScalar::symmetric_quadratic_form`].
633    fn symmetric_quadratic_form<C: SymmetricQuadraticCoefficients>(
634        inputs: &[Self],
635        coefficients: &C,
636        dimension: usize,
637        workspace: &'arena Self::Workspace,
638    ) -> Self {
639        symmetric_quadratic_form_default(
640            inputs,
641            coefficients,
642            |value| Self::constant(value, dimension, workspace),
643            Self::add,
644            Self::mul,
645            Self::scale,
646        )
647    }
648
649    /// Evaluate `sum_i weights[i] * inputs[i]` in one semantic primitive.
650    fn linear_combination(
651        inputs: &[Self],
652        weights: &[f64],
653        dimension: usize,
654        workspace: &'arena Self::Workspace,
655    ) -> Self {
656        linear_combination_default(
657            inputs,
658            weights,
659            |value| Self::constant(value, dimension, workspace),
660            Self::add,
661            Self::scale,
662        )
663    }
664
665    /// Add a primal constant without changing derivative channels.
666    fn add_constant(&self, constant: f64) -> Self {
667        self.with_value(self.value() + constant)
668    }
669
670    /// Evaluate `self * right + addend` in one semantic primitive.
671    fn multiply_add(&self, right: &Self, addend: &Self) -> Self {
672        multiply_add_default(self, right, addend, Self::mul, Self::add)
673    }
674
675    /// Sum unary compositions directly from certified derivative stacks.
676    fn composed_sum(
677        inputs: &[Self],
678        derivative_stacks: &[[f64; 5]],
679        dimension: usize,
680        workspace: &'arena Self::Workspace,
681    ) -> Self {
682        composed_sum_default(
683            inputs,
684            derivative_stacks,
685            |value| Self::constant(value, dimension, workspace),
686            Self::add,
687            Self::compose_unary,
688        )
689    }
690
691    /// Exact product as an explicit compiled graph node.
692    fn product(&self, right: &Self) -> Self {
693        self.mul(right)
694    }
695
696    /// Compose a certified outer stack after an affine input map.
697    fn affine_compose(
698        &self,
699        input_scale: f64,
700        input_shift: f64,
701        derivative_stack: [f64; 5],
702    ) -> Self {
703        affine_compose_default(
704            self,
705            input_scale,
706            input_shift,
707            derivative_stack,
708            Self::scale,
709            |value, constant| value.add_constant(constant),
710            Self::compose_unary,
711        )
712    }
713
714    /// Sum unary compositions whose inputs each carry an affine scale.
715    fn affine_composed_sum(
716        inputs: &[Self],
717        input_scales: &[f64],
718        derivative_stacks: &[[f64; 5]],
719        dimension: usize,
720        workspace: &'arena Self::Workspace,
721    ) -> Self {
722        affine_composed_sum_default(
723            inputs,
724            input_scales,
725            derivative_stacks,
726            |value| Self::constant(value, dimension, workspace),
727            Self::add,
728            Self::scale,
729            |value, constant| value.add_constant(constant),
730            Self::compose_unary,
731        )
732    }
733
734    /// Runtime-dimension lowering of
735    /// `Σ_i f_i(input_scale_i · (left_i · right + addend_scale_i · addend))`
736    /// from certified derivative stacks. Const expression arity replaces an
737    /// implementation-specific term cap, while shared operands expose universal
738    /// common-subexpression elimination. If every addend scale is exact zero
739    /// (including `-0.0`), the addend has no dimension, workspace, or
740    /// derivative-channel obligations.
741    fn shared_multiply_add_affine_composed_sum<const N: usize>(
742        lefts: &[&Self; N],
743        right: &Self,
744        addend: &Self,
745        addend_scales: &[f64; N],
746        input_scales: &[f64; N],
747        derivative_stacks: &[[f64; 5]; N],
748        dimension: usize,
749        workspace: &'arena Self::Workspace,
750    ) -> Self {
751        shared_multiply_add_affine_composed_sum_default(
752            lefts,
753            right,
754            addend,
755            addend_scales,
756            input_scales,
757            derivative_stacks,
758            |value| Self::constant(value, dimension, workspace),
759            Self::add,
760            Self::mul,
761            Self::scale,
762            Self::multiply_add,
763            |input, scale, shift, stack| input.affine_compose(scale, shift, stack),
764        )
765    }
766    /// Number of primary derivative axes carried by this scalar.
767    fn dimension(&self) -> usize;
768    /// Value channel.
769    fn value(&self) -> f64;
770    /// Exact truncated sum.
771    fn add(&self, o: &Self) -> Self;
772    /// Exact truncated difference.
773    fn sub(&self, o: &Self) -> Self;
774    /// Exact truncated product.
775    fn mul(&self, o: &Self) -> Self;
776    /// Negate every channel.
777    fn neg(&self) -> Self;
778    /// Scale every channel.
779    fn scale(&self, s: f64) -> Self;
780    /// Exact unary composition from the certified derivative stack.
781    fn compose_unary(&self, d: [f64; 5]) -> Self;
782
783    /// `e^self`.
784    fn exp(&self) -> Self {
785        let e = self.value().exp();
786        self.compose_unary([e, e, e, e, e])
787    }
788
789    /// `ln(self)`. Caller guarantees positivity. This is the runtime-width
790    /// counterpart of [`JetScalar::ln`] and uses the identical certified
791    /// derivative stack.
792    fn ln(&self) -> Self {
793        let u = self.value();
794        let r = 1.0 / u;
795        self.compose_unary([u.ln(), r, -r * r, 2.0 * r * r * r, -6.0 * r * r * r * r])
796    }
797
798    /// `1/self`.
799    fn recip(&self) -> Self {
800        let r = 1.0 / self.value();
801        let r2 = r * r;
802        self.compose_unary([r, -r2, 2.0 * r2 * r, -6.0 * r2 * r2, 24.0 * r2 * r2 * r])
803    }
804}
805
806/// Zero-order runtime scalar for evaluating the primal value of a canonical
807/// runtime-width row program without constructing derivative channels.
808///
809/// The primary dimension remains part of the scalar so it obeys the same
810/// runtime algebra contract as derivative-carrying implementations. Unary
811/// derivative stacks contribute only their value entry, while structured
812/// quadratic operators retain their representation-specific primal lowering.
813#[derive(Clone, Copy, Debug, PartialEq)]
814pub struct RuntimeValue {
815    value: f64,
816    dimension: usize,
817}
818
819impl<'arena> RuntimeJetScalar<'arena> for RuntimeValue {
820    type Workspace = ();
821
822    #[inline(always)]
823    fn constant(c: f64, dimension: usize, &(): &'arena Self::Workspace) -> Self {
824        Self {
825            value: c,
826            dimension,
827        }
828    }
829
830    #[inline(always)]
831    fn variable(x: f64, axis: usize, dimension: usize, &(): &'arena Self::Workspace) -> Self {
832        assert!(
833            axis < dimension,
834            "runtime value variable axis out of bounds"
835        );
836        Self {
837            value: x,
838            dimension,
839        }
840    }
841
842    #[inline(always)]
843    fn constant_like(&self, c: f64) -> Self {
844        Self {
845            value: c,
846            dimension: self.dimension,
847        }
848    }
849
850    #[inline(always)]
851    fn with_value(&self, value: f64) -> Self {
852        Self {
853            value,
854            dimension: self.dimension,
855        }
856    }
857
858    #[inline(always)]
859    fn symmetric_quadratic_form<C: SymmetricQuadraticCoefficients>(
860        inputs: &[Self],
861        coefficients: &C,
862        dimension: usize,
863        &(): &'arena Self::Workspace,
864    ) -> Self {
865        assert_eq!(inputs.len(), coefficients.dimension());
866        assert!(inputs.iter().all(|input| input.dimension == dimension));
867        Self {
868            value: coefficients.quadratic_value(inputs, |input| input.value),
869            dimension,
870        }
871    }
872
873    #[inline(always)]
874    fn linear_combination(
875        inputs: &[Self],
876        weights: &[f64],
877        dimension: usize,
878        &(): &'arena Self::Workspace,
879    ) -> Self {
880        assert_eq!(inputs.len(), weights.len());
881        assert!(inputs.iter().all(|input| input.dimension == dimension));
882        let value = inputs
883            .iter()
884            .zip(weights)
885            .map(|(input, &weight)| input.value * weight)
886            .sum();
887        Self { value, dimension }
888    }
889
890    #[inline(always)]
891    fn multiply_add(&self, right: &Self, addend: &Self) -> Self {
892        self.assert_same_dimension(right);
893        self.assert_same_dimension(addend);
894        Self {
895            value: self.value * right.value + addend.value,
896            dimension: self.dimension,
897        }
898    }
899
900    #[inline(always)]
901    fn composed_sum(
902        inputs: &[Self],
903        derivative_stacks: &[[f64; 5]],
904        dimension: usize,
905        &(): &'arena Self::Workspace,
906    ) -> Self {
907        assert_eq!(inputs.len(), derivative_stacks.len());
908        assert!(inputs.iter().all(|input| input.dimension == dimension));
909        Self {
910            value: derivative_stacks.iter().map(|stack| stack[0]).sum(),
911            dimension,
912        }
913    }
914
915    #[inline(always)]
916    fn product(&self, right: &Self) -> Self {
917        self.mul(right)
918    }
919
920    #[inline(always)]
921    fn affine_compose(
922        &self,
923        input_scale: f64,
924        input_shift: f64,
925        derivative_stack: [f64; 5],
926    ) -> Self {
927        // Route the affine pre-composition through the shared default instead
928        // of hand-rolling the zero-order shortcut: `compose_unary` already
929        // reduces to `derivative_stack[0]` for this scalar, so the result is
930        // unchanged, but the scale and shift now travel the same path every
931        // other implementation uses rather than being dropped on the floor.
932        affine_compose_default(
933            self,
934            input_scale,
935            input_shift,
936            derivative_stack,
937            Self::scale,
938            |value, constant| value.add_constant(constant),
939            Self::compose_unary,
940        )
941    }
942
943    #[inline(always)]
944    fn affine_composed_sum(
945        inputs: &[Self],
946        input_scales: &[f64],
947        derivative_stacks: &[[f64; 5]],
948        dimension: usize,
949        &(): &'arena Self::Workspace,
950    ) -> Self {
951        assert_eq!(inputs.len(), input_scales.len());
952        assert_eq!(inputs.len(), derivative_stacks.len());
953        assert!(inputs.iter().all(|input| input.dimension == dimension));
954        Self {
955            value: derivative_stacks.iter().map(|stack| stack[0]).sum(),
956            dimension,
957        }
958    }
959
960    #[inline(always)]
961    fn dimension(&self) -> usize {
962        self.dimension
963    }
964
965    #[inline(always)]
966    fn value(&self) -> f64 {
967        self.value
968    }
969
970    #[inline(always)]
971    fn add(&self, other: &Self) -> Self {
972        self.assert_same_dimension(other);
973        Self {
974            value: self.value + other.value,
975            dimension: self.dimension,
976        }
977    }
978
979    #[inline(always)]
980    fn sub(&self, other: &Self) -> Self {
981        self.assert_same_dimension(other);
982        Self {
983            value: self.value - other.value,
984            dimension: self.dimension,
985        }
986    }
987
988    #[inline(always)]
989    fn mul(&self, other: &Self) -> Self {
990        self.assert_same_dimension(other);
991        Self {
992            value: self.value * other.value,
993            dimension: self.dimension,
994        }
995    }
996
997    #[inline(always)]
998    fn neg(&self) -> Self {
999        Self {
1000            value: -self.value,
1001            dimension: self.dimension,
1002        }
1003    }
1004
1005    #[inline(always)]
1006    fn scale(&self, scale: f64) -> Self {
1007        Self {
1008            value: self.value * scale,
1009            dimension: self.dimension,
1010        }
1011    }
1012
1013    #[inline(always)]
1014    fn compose_unary(&self, derivative_stack: [f64; 5]) -> Self {
1015        Self {
1016            value: derivative_stack[0],
1017            dimension: self.dimension,
1018        }
1019    }
1020}
1021
1022impl RuntimeValue {
1023    #[inline(always)]
1024    fn assert_same_dimension(&self, other: &Self) {
1025        assert_eq!(self.dimension, other.dimension);
1026    }
1027}
1028
1029/// Adapter that presents any const-generic [`JetScalar<K>`] through the
1030/// runtime-dimension interface.  It is used by derivative oracles so the same
1031/// row program can be instantiated at a fixed tower and at a dynamic packed
1032/// scalar; production code unwraps the inner fixed tower after evaluation.
1033#[derive(Clone, Copy, Debug)]
1034#[repr(transparent)]
1035pub struct FixedRuntimeJet<S, const K: usize> {
1036    inner: S,
1037}
1038
1039impl<S, const K: usize> FixedRuntimeJet<S, K> {
1040    /// Wrap a fixed-dimension scalar so a runtime-dimension row expression can
1041    /// evaluate it without authoring a second const-generic body.
1042    #[inline(always)]
1043    #[must_use]
1044    pub fn from_inner(inner: S) -> Self {
1045        Self { inner }
1046    }
1047
1048    /// Recover the wrapped const-generic scalar.
1049    #[inline(always)]
1050    #[must_use]
1051    pub fn into_inner(self) -> S {
1052        self.inner
1053    }
1054}
1055
1056impl<'arena, S: JetScalar<K>, const K: usize> RuntimeJetScalar<'arena> for FixedRuntimeJet<S, K> {
1057    type Workspace = ();
1058
1059    #[inline(always)]
1060    fn constant(c: f64, dimension: usize, &(): &'arena Self::Workspace) -> Self {
1061        assert_eq!(dimension, K, "fixed jet dimension mismatch");
1062        Self {
1063            inner: S::constant(c),
1064        }
1065    }
1066
1067    #[inline(always)]
1068    fn variable(x: f64, axis: usize, dimension: usize, &(): &'arena Self::Workspace) -> Self {
1069        assert_eq!(dimension, K, "fixed jet dimension mismatch");
1070        Self {
1071            inner: S::variable(x, axis),
1072        }
1073    }
1074
1075    #[inline(always)]
1076    fn constant_like(&self, c: f64) -> Self {
1077        Self {
1078            inner: S::constant(c),
1079        }
1080    }
1081
1082    #[inline(always)]
1083    fn with_value(&self, value: f64) -> Self {
1084        Self {
1085            inner: self.inner.compose_unary([value, 1.0, 0.0, 0.0, 0.0]),
1086        }
1087    }
1088
1089    #[inline(always)]
1090    fn symmetric_quadratic_form<C: SymmetricQuadraticCoefficients>(
1091        inputs: &[Self],
1092        coefficients: &C,
1093        dimension: usize,
1094        &(): &'arena Self::Workspace,
1095    ) -> Self {
1096        assert_eq!(dimension, K, "fixed jet dimension mismatch");
1097        assert_eq!(inputs.len(), coefficients.dimension());
1098        // SAFETY: `FixedRuntimeJet<S, K>` is `repr(transparent)` with exactly one
1099        // non-zero-sized field of type `S`. Therefore each element has the same
1100        // layout and alignment as `S`; the cast preserves the allocation,
1101        // provenance, element count, and shared lifetime of `inputs`, and no
1102        // mutable reference is created while the original slice is borrowed.
1103        let inner =
1104            unsafe { std::slice::from_raw_parts(inputs.as_ptr().cast::<S>(), inputs.len()) };
1105        Self {
1106            inner: S::symmetric_quadratic_form(inner, coefficients),
1107        }
1108    }
1109
1110    #[inline(always)]
1111    fn linear_combination(
1112        inputs: &[Self],
1113        weights: &[f64],
1114        dimension: usize,
1115        &(): &'arena Self::Workspace,
1116    ) -> Self {
1117        assert_eq!(dimension, K, "fixed jet dimension mismatch");
1118        assert_eq!(inputs.len(), weights.len());
1119        // SAFETY: `FixedRuntimeJet<S, K>` is `repr(transparent)` over its sole
1120        // non-zero-sized `S` field, so the shared slice has identical element
1121        // layout, alignment, provenance, length, and lifetime after the cast.
1122        let inner =
1123            unsafe { std::slice::from_raw_parts(inputs.as_ptr().cast::<S>(), inputs.len()) };
1124        Self {
1125            inner: S::linear_combination(inner, weights),
1126        }
1127    }
1128
1129    #[inline(always)]
1130    fn add_constant(&self, constant: f64) -> Self {
1131        Self {
1132            inner: self.inner.add_constant(constant),
1133        }
1134    }
1135
1136    #[inline(always)]
1137    fn multiply_add(&self, right: &Self, addend: &Self) -> Self {
1138        Self {
1139            inner: self.inner.multiply_add(&right.inner, &addend.inner),
1140        }
1141    }
1142
1143    #[inline(always)]
1144    fn composed_sum(
1145        inputs: &[Self],
1146        derivative_stacks: &[[f64; 5]],
1147        dimension: usize,
1148        &(): &'arena Self::Workspace,
1149    ) -> Self {
1150        assert_eq!(dimension, K, "fixed jet dimension mismatch");
1151        // SAFETY: `FixedRuntimeJet<S, K>` is `repr(transparent)` over its sole
1152        // non-zero-sized `S` field, so the shared slice has identical element
1153        // layout, alignment, provenance, length, and lifetime after the cast.
1154        let inner =
1155            unsafe { std::slice::from_raw_parts(inputs.as_ptr().cast::<S>(), inputs.len()) };
1156        Self {
1157            inner: S::composed_sum(inner, derivative_stacks),
1158        }
1159    }
1160
1161    #[inline(always)]
1162    fn product(&self, right: &Self) -> Self {
1163        Self {
1164            inner: self.inner.product(&right.inner),
1165        }
1166    }
1167
1168    #[inline(always)]
1169    fn affine_compose(
1170        &self,
1171        input_scale: f64,
1172        input_shift: f64,
1173        derivative_stack: [f64; 5],
1174    ) -> Self {
1175        Self {
1176            inner: self
1177                .inner
1178                .affine_compose(input_scale, input_shift, derivative_stack),
1179        }
1180    }
1181
1182    #[inline(always)]
1183    fn affine_composed_sum(
1184        inputs: &[Self],
1185        input_scales: &[f64],
1186        derivative_stacks: &[[f64; 5]],
1187        dimension: usize,
1188        &(): &'arena Self::Workspace,
1189    ) -> Self {
1190        assert_eq!(dimension, K, "fixed jet dimension mismatch");
1191        // SAFETY: `FixedRuntimeJet<S, K>` is `repr(transparent)` over its sole
1192        // non-zero-sized `S` field, so the shared slice has identical element
1193        // layout, alignment, provenance, length, and lifetime after the cast.
1194        let inner =
1195            unsafe { std::slice::from_raw_parts(inputs.as_ptr().cast::<S>(), inputs.len()) };
1196        Self {
1197            inner: S::affine_composed_sum(inner, input_scales, derivative_stacks),
1198        }
1199    }
1200
1201    #[inline(always)]
1202    fn shared_multiply_add_affine_composed_sum<const N: usize>(
1203        lefts: &[&Self; N],
1204        right: &Self,
1205        addend: &Self,
1206        addend_scales: &[f64; N],
1207        input_scales: &[f64; N],
1208        derivative_stacks: &[[f64; 5]; N],
1209        dimension: usize,
1210        &(): &'arena Self::Workspace,
1211    ) -> Self {
1212        assert_eq!(dimension, K, "fixed jet dimension mismatch");
1213        let left_inner: [&S; N] = std::array::from_fn(|term| &lefts[term].inner);
1214        Self {
1215            inner: S::shared_multiply_add_affine_composed_sum(
1216                &left_inner,
1217                &right.inner,
1218                &addend.inner,
1219                addend_scales,
1220                input_scales,
1221                derivative_stacks,
1222            ),
1223        }
1224    }
1225
1226    #[inline(always)]
1227    fn dimension(&self) -> usize {
1228        K
1229    }
1230
1231    #[inline(always)]
1232    fn value(&self) -> f64 {
1233        self.inner.value()
1234    }
1235
1236    #[inline(always)]
1237    fn add(&self, o: &Self) -> Self {
1238        Self {
1239            inner: self.inner.add(&o.inner),
1240        }
1241    }
1242
1243    #[inline(always)]
1244    fn sub(&self, o: &Self) -> Self {
1245        Self {
1246            inner: self.inner.sub(&o.inner),
1247        }
1248    }
1249
1250    #[inline(always)]
1251    fn mul(&self, o: &Self) -> Self {
1252        Self {
1253            inner: self.inner.mul(&o.inner),
1254        }
1255    }
1256
1257    #[inline(always)]
1258    fn neg(&self) -> Self {
1259        Self {
1260            inner: self.inner.neg(),
1261        }
1262    }
1263
1264    #[inline(always)]
1265    fn scale(&self, s: f64) -> Self {
1266        Self {
1267            inner: self.inner.scale(s),
1268        }
1269    }
1270
1271    #[inline(always)]
1272    fn compose_unary(&self, d: [f64; 5]) -> Self {
1273        Self {
1274            inner: self.inner.compose_unary(d),
1275        }
1276    }
1277}
1278
1279/// Reusable storage for runtime-sized packed jets. Scalar primitives write
1280/// into this bump arena, so arithmetic performs no heap allocation. Callers
1281/// reserve once per worker/chunk and [`reset`](Self::reset) between rows.
1282#[derive(Debug)]
1283pub struct DynamicJetArena {
1284    bump: bumpalo::Bump,
1285}
1286
1287impl DynamicJetArena {
1288    /// Create an arena with the allocator's default initial chunk.
1289    #[must_use]
1290    pub fn new() -> Self {
1291        Self {
1292            bump: bumpalo::Bump::new(),
1293        }
1294    }
1295
1296    /// Create an arena with a row-program-selected initial byte capacity.
1297    #[must_use]
1298    pub fn with_capacity(bytes: usize) -> Self {
1299        Self {
1300            bump: bumpalo::Bump::with_capacity(bytes),
1301        }
1302    }
1303
1304    /// Reclaim all scalar outputs and compact a fragmented high-water mark
1305    /// into one retained chunk.
1306    ///
1307    /// `bumpalo::Bump::reset` keeps only its newest chunk and returns every
1308    /// older chunk to the global allocator. Runtime jet tapes routinely span
1309    /// several geometrically-grown chunks, so a bare reset would allocate
1310    /// those discarded chunks again on every row. When reset exposes that
1311    /// fragmentation, replace the empty arena once with a single chunk large
1312    /// enough for the complete prior tape. Later equal-or-smaller rows reuse
1313    /// that chunk without allocator traffic.
1314    pub fn reset(&mut self) {
1315        let high_water = self.bump.allocated_bytes();
1316        self.bump.reset();
1317        if self.bump.allocated_bytes() < high_water {
1318            self.bump = bumpalo::Bump::with_capacity(high_water);
1319        }
1320    }
1321
1322    /// Bytes currently reserved from the global allocator. A warm-reset-warm
1323    /// benchmark uses this to prove the second row requires no arena growth.
1324    #[must_use]
1325    pub fn allocated_bytes(&self) -> usize {
1326        self.bump.allocated_bytes()
1327    }
1328
1329    #[inline(always)]
1330    fn zeros(&self, len: usize) -> &mut [f64] {
1331        self.bump.alloc_slice_fill_copy(len, 0.0)
1332    }
1333
1334    /// Allocate and initialize a runtime-sized slice in the arena. Row programs
1335    /// use this for their primary-scalar arrays so those arrays share the same
1336    /// reusable workspace as derivative channels.
1337    #[inline(always)]
1338    pub fn alloc_slice_fill_with<T>(&self, len: usize, fill: impl FnMut(usize) -> T) -> &mut [T] {
1339        self.bump.alloc_slice_fill_with(len, fill)
1340    }
1341}
1342
1343impl Default for DynamicJetArena {
1344    fn default() -> Self {
1345        Self::new()
1346    }
1347}
1348
1349/// Runtime-sized packed first-order scalar: value plus arena-backed gradient.
1350#[derive(Clone, Copy, Debug)]
1351pub struct DynamicOrder1<'arena> {
1352    arena: &'arena DynamicJetArena,
1353    /// Value channel.
1354    pub v: f64,
1355    /// Gradient channel, length [`Self::dimension`].
1356    pub g: &'arena [f64],
1357}
1358
1359impl DynamicOrder1<'_> {
1360    /// Gradient channel.
1361    #[inline]
1362    #[must_use]
1363    pub fn g(&self) -> &[f64] {
1364        self.g
1365    }
1366
1367    #[inline]
1368    fn assert_compatible(&self, o: &Self) {
1369        assert_eq!(
1370            self.g.len(),
1371            o.g.len(),
1372            "dynamic first-order jet dimension mismatch"
1373        );
1374        assert!(
1375            std::ptr::eq(self.arena, o.arena),
1376            "dynamic jets belong to different arenas"
1377        );
1378    }
1379}
1380
1381impl<'arena> RuntimeJetScalar<'arena> for DynamicOrder1<'arena> {
1382    type Workspace = DynamicJetArena;
1383
1384    fn constant(c: f64, dimension: usize, arena: &'arena DynamicJetArena) -> Self {
1385        Self {
1386            arena,
1387            v: c,
1388            g: arena.zeros(dimension),
1389        }
1390    }
1391
1392    fn variable(x: f64, axis: usize, dimension: usize, arena: &'arena DynamicJetArena) -> Self {
1393        assert!(
1394            axis < dimension,
1395            "dynamic first-order jet axis out of bounds"
1396        );
1397        let g = arena.zeros(dimension);
1398        g[axis] = 1.0;
1399        Self { arena, v: x, g }
1400    }
1401
1402    #[inline(always)]
1403    fn constant_like(&self, c: f64) -> Self {
1404        Self {
1405            arena: self.arena,
1406            v: c,
1407            g: self.arena.zeros(self.dimension()),
1408        }
1409    }
1410
1411    #[inline(always)]
1412    fn with_value(&self, value: f64) -> Self {
1413        Self {
1414            arena: self.arena,
1415            v: value,
1416            g: self.g,
1417        }
1418    }
1419
1420    fn dimension(&self) -> usize {
1421        self.g.len()
1422    }
1423    fn value(&self) -> f64 {
1424        self.v
1425    }
1426
1427    fn add(&self, o: &Self) -> Self {
1428        self.assert_compatible(o);
1429        let g = self.arena.zeros(self.dimension());
1430        for i in 0..g.len() {
1431            g[i] = self.g[i] + o.g[i];
1432        }
1433        Self {
1434            arena: self.arena,
1435            v: self.v + o.v,
1436            g,
1437        }
1438    }
1439
1440    fn sub(&self, o: &Self) -> Self {
1441        self.assert_compatible(o);
1442        let g = self.arena.zeros(self.dimension());
1443        for i in 0..g.len() {
1444            g[i] = self.g[i] - o.g[i];
1445        }
1446        Self {
1447            arena: self.arena,
1448            v: self.v - o.v,
1449            g,
1450        }
1451    }
1452
1453    fn mul(&self, o: &Self) -> Self {
1454        self.assert_compatible(o);
1455        let g = self.arena.zeros(self.dimension());
1456        for i in 0..g.len() {
1457            g[i] = self.v * o.g[i] + self.g[i] * o.v;
1458        }
1459        Self {
1460            arena: self.arena,
1461            v: self.v * o.v,
1462            g,
1463        }
1464    }
1465
1466    fn neg(&self) -> Self {
1467        self.scale(-1.0)
1468    }
1469
1470    fn scale(&self, s: f64) -> Self {
1471        let g = self.arena.zeros(self.dimension());
1472        for i in 0..g.len() {
1473            g[i] = self.g[i] * s;
1474        }
1475        Self {
1476            arena: self.arena,
1477            v: self.v * s,
1478            g,
1479        }
1480    }
1481
1482    fn compose_unary(&self, d: [f64; 5]) -> Self {
1483        let g = self.arena.zeros(self.dimension());
1484        for i in 0..g.len() {
1485            g[i] = d[1] * self.g[i];
1486        }
1487        Self {
1488            arena: self.arena,
1489            v: d[0],
1490            g,
1491        }
1492    }
1493}
1494
1495/// Runtime-sized packed second-order scalar: value, gradient, and a row-major
1496/// Hessian. Storage is `O(K^2)` in the row's actual primary dimension and comes
1497/// from the row's reusable [`DynamicJetArena`].
1498#[derive(Clone, Copy, Debug)]
1499pub struct DynamicOrder2<'arena> {
1500    arena: &'arena DynamicJetArena,
1501    /// Value channel.
1502    pub v: f64,
1503    /// Gradient channel.
1504    pub g: &'arena [f64],
1505    /// Row-major Hessian channel.
1506    pub h: &'arena [f64],
1507}
1508
1509impl DynamicOrder2<'_> {
1510    /// Construct an arena-backed second-order scalar from channel functions.
1511    ///
1512    /// This is the allocation-free extension seam for exact projected products
1513    /// whose channel law is not ordinary scalar multiplication. Each channel is
1514    /// written directly into the row arena, so downstream runtime-jet programs
1515    /// can keep their specialized algebra without materializing temporary
1516    /// `Vec`s or exposing the arena pointer stored by [`DynamicOrder2`]. The
1517    /// Hessian function is evaluated on the upper triangle and mirrored because
1518    /// a scalar Hessian is symmetric.
1519    #[inline]
1520    #[must_use]
1521    pub fn from_channel_functions<'arena>(
1522        value: f64,
1523        dimension: usize,
1524        arena: &'arena DynamicJetArena,
1525        mut gradient: impl FnMut(usize) -> f64,
1526        mut hessian: impl FnMut(usize, usize) -> f64,
1527    ) -> DynamicOrder2<'arena> {
1528        let g = arena.alloc_slice_fill_with(dimension, |axis| gradient(axis));
1529        let h = arena.zeros(dimension * dimension);
1530        for row in 0..dimension {
1531            for column in row..dimension {
1532                let channel = hessian(row, column);
1533                h[row * dimension + column] = channel;
1534                h[column * dimension + row] = channel;
1535            }
1536        }
1537        DynamicOrder2 {
1538            arena,
1539            v: value,
1540            g,
1541            h,
1542        }
1543    }
1544
1545    /// Gradient channel.
1546    #[inline]
1547    #[must_use]
1548    pub fn g(&self) -> &[f64] {
1549        self.g
1550    }
1551
1552    /// Row-major Hessian channel.
1553    #[inline]
1554    #[must_use]
1555    pub fn h(&self) -> &[f64] {
1556        self.h
1557    }
1558
1559    /// Hessian entry `(row, col)`.
1560    #[inline]
1561    #[must_use]
1562    pub fn h_at(&self, row: usize, col: usize) -> f64 {
1563        self.h[row * self.dimension() + col]
1564    }
1565
1566    #[inline(always)]
1567    fn assert_compatible(&self, o: &Self) {
1568        assert_eq!(
1569            self.g.len(),
1570            o.g.len(),
1571            "dynamic second-order jet dimension mismatch"
1572        );
1573        assert_eq!(
1574            self.h.len(),
1575            o.h.len(),
1576            "dynamic second-order jet Hessian mismatch"
1577        );
1578        assert!(
1579            std::ptr::eq(self.arena, o.arena),
1580            "dynamic jets belong to different arenas"
1581        );
1582    }
1583}
1584
1585impl<'arena> RuntimeJetScalar<'arena> for DynamicOrder2<'arena> {
1586    type Workspace = DynamicJetArena;
1587
1588    #[inline(always)]
1589    fn constant(c: f64, dimension: usize, arena: &'arena DynamicJetArena) -> Self {
1590        Self {
1591            arena,
1592            v: c,
1593            g: arena.zeros(dimension),
1594            h: arena.zeros(dimension * dimension),
1595        }
1596    }
1597
1598    #[inline(always)]
1599    fn variable(x: f64, axis: usize, dimension: usize, arena: &'arena DynamicJetArena) -> Self {
1600        assert!(
1601            axis < dimension,
1602            "dynamic second-order jet axis out of bounds"
1603        );
1604        let g = arena.zeros(dimension);
1605        g[axis] = 1.0;
1606        Self {
1607            arena,
1608            v: x,
1609            g,
1610            h: arena.zeros(dimension * dimension),
1611        }
1612    }
1613
1614    #[inline(always)]
1615    fn constant_like(&self, c: f64) -> Self {
1616        let dimension = self.dimension();
1617        Self {
1618            arena: self.arena,
1619            v: c,
1620            g: self.arena.zeros(dimension),
1621            h: self.arena.zeros(dimension * dimension),
1622        }
1623    }
1624
1625    #[inline(always)]
1626    fn with_value(&self, value: f64) -> Self {
1627        Self {
1628            arena: self.arena,
1629            v: value,
1630            g: self.g,
1631            h: self.h,
1632        }
1633    }
1634
1635    #[inline(always)]
1636    fn symmetric_quadratic_form<C: SymmetricQuadraticCoefficients>(
1637        inputs: &[Self],
1638        coefficients: &C,
1639        dimension: usize,
1640        arena: &'arena DynamicJetArena,
1641    ) -> Self {
1642        assert_eq!(inputs.len(), coefficients.dimension());
1643        assert!(
1644            inputs.iter().all(|input| {
1645                input.dimension() == dimension && std::ptr::eq(input.arena, arena)
1646            }),
1647            "dynamic quadratic-form jets must share dimension and arena"
1648        );
1649        let input_dimension = inputs.len();
1650        let values = arena.zeros(input_dimension);
1651        for (value, input) in values.iter_mut().zip(inputs) {
1652            *value = input.v;
1653        }
1654        let projected = arena.zeros(input_dimension);
1655        coefficients.multiply(values, projected);
1656
1657        let mut value = 0.0;
1658        for axis in 0..input_dimension {
1659            value += values[axis] * projected[axis];
1660        }
1661        let gradient = arena.zeros(dimension);
1662        for primary in 0..dimension {
1663            let mut channel = 0.0;
1664            for axis in 0..input_dimension {
1665                channel += projected[axis] * inputs[axis].g[primary];
1666            }
1667            gradient[primary] = 2.0 * channel;
1668        }
1669        let hessian = arena.zeros(dimension * dimension);
1670        let input_gradient = arena.zeros(input_dimension);
1671        let projected_gradient = arena.zeros(input_dimension);
1672        for primary_b in 0..dimension {
1673            for row in 0..input_dimension {
1674                input_gradient[row] = inputs[row].g[primary_b];
1675            }
1676            coefficients.multiply(input_gradient, projected_gradient);
1677            for primary_a in 0..=primary_b {
1678                let mut inherited = 0.0;
1679                let mut curvature = 0.0;
1680                for row in 0..input_dimension {
1681                    inherited += projected[row] * inputs[row].h[primary_a * dimension + primary_b];
1682                    curvature += inputs[row].g[primary_a] * projected_gradient[row];
1683                }
1684                let channel = 2.0 * (inherited + curvature);
1685                hessian[primary_a * dimension + primary_b] = channel;
1686                hessian[primary_b * dimension + primary_a] = channel;
1687            }
1688        }
1689        Self {
1690            arena,
1691            v: value,
1692            g: gradient,
1693            h: hessian,
1694        }
1695    }
1696
1697    #[inline(always)]
1698    fn product(&self, right: &Self) -> Self {
1699        self.assert_compatible(right);
1700        let dimension = self.dimension();
1701        let gradient = self.arena.zeros(dimension);
1702        let hessian = self.arena.zeros(dimension * dimension);
1703        for primary in 0..dimension {
1704            gradient[primary] = self.v * right.g[primary] + self.g[primary] * right.v;
1705            for other in primary..dimension {
1706                let index = primary * dimension + other;
1707                let channel = self.v * right.h[index]
1708                    + self.g[primary] * right.g[other]
1709                    + self.g[other] * right.g[primary]
1710                    + self.h[index] * right.v;
1711                hessian[index] = channel;
1712                hessian[other * dimension + primary] = channel;
1713            }
1714        }
1715        Self {
1716            arena: self.arena,
1717            v: self.v * right.v,
1718            g: gradient,
1719            h: hessian,
1720        }
1721    }
1722
1723    #[inline(always)]
1724    fn affine_compose(
1725        &self,
1726        input_scale: f64,
1727        input_shift: f64,
1728        derivative_stack: [f64; 5],
1729    ) -> Self {
1730        assert!(input_shift.is_finite(), "affine input shift must be finite");
1731        let arena = self.arena;
1732        let dimension = self.dimension();
1733        let first = derivative_stack[1] * input_scale;
1734        let second = derivative_stack[2] * input_scale * input_scale;
1735        let gradient = arena.zeros(dimension);
1736        let hessian = arena.zeros(dimension * dimension);
1737        for primary in 0..dimension {
1738            gradient[primary] = first * self.g[primary];
1739            for other in primary..dimension {
1740                let index = primary * dimension + other;
1741                let channel = first * self.h[index] + second * self.g[primary] * self.g[other];
1742                hessian[index] = channel;
1743                hessian[other * dimension + primary] = channel;
1744            }
1745        }
1746        Self {
1747            arena,
1748            v: derivative_stack[0],
1749            g: gradient,
1750            h: hessian,
1751        }
1752    }
1753
1754    #[inline(always)]
1755    fn affine_composed_sum(
1756        inputs: &[Self],
1757        input_scales: &[f64],
1758        derivative_stacks: &[[f64; 5]],
1759        dimension: usize,
1760        arena: &'arena DynamicJetArena,
1761    ) -> Self {
1762        assert_eq!(inputs.len(), input_scales.len());
1763        assert_eq!(inputs.len(), derivative_stacks.len());
1764        assert!(
1765            inputs.iter().all(|input| {
1766                input.dimension() == dimension && std::ptr::eq(input.arena, arena)
1767            }),
1768            "dynamic affine-composed-sum jets must share dimension and arena"
1769        );
1770        let gradient = arena.zeros(dimension);
1771        let hessian = arena.zeros(dimension * dimension);
1772        let mut value = 0.0;
1773        for ((input, &input_scale), stack) in inputs.iter().zip(input_scales).zip(derivative_stacks)
1774        {
1775            let first = stack[1] * input_scale;
1776            let second = stack[2] * input_scale * input_scale;
1777            value += stack[0];
1778            for primary in 0..dimension {
1779                gradient[primary] += first * input.g[primary];
1780                for other in primary..dimension {
1781                    let index = primary * dimension + other;
1782                    hessian[index] +=
1783                        first * input.h[index] + second * input.g[primary] * input.g[other];
1784                }
1785            }
1786        }
1787        for primary in 0..dimension {
1788            for other in primary + 1..dimension {
1789                hessian[other * dimension + primary] = hessian[primary * dimension + other];
1790            }
1791        }
1792        Self {
1793            arena,
1794            v: value,
1795            g: gradient,
1796            h: hessian,
1797        }
1798    }
1799
1800    #[inline(always)]
1801    fn shared_multiply_add_affine_composed_sum<const N: usize>(
1802        lefts: &[&Self; N],
1803        right: &Self,
1804        addend: &Self,
1805        addend_scales: &[f64; N],
1806        input_scales: &[f64; N],
1807        derivative_stacks: &[[f64; 5]; N],
1808        dimension: usize,
1809        arena: &'arena DynamicJetArena,
1810    ) -> Self {
1811        assert!(
1812            lefts.iter().all(|input| {
1813                input.dimension() == dimension && std::ptr::eq(input.arena, arena)
1814            }) && (N == 0 || (right.dimension() == dimension && std::ptr::eq(right.arena, arena))),
1815            "dynamic fused product-composition jets must share dimension and arena"
1816        );
1817        let addend_live = addend_scales.iter().any(|&scale| scale != 0.0);
1818        assert!(
1819            !addend_live || (addend.dimension() == dimension && std::ptr::eq(addend.arena, arena)),
1820            "live dynamic fused addends must share dimension and arena"
1821        );
1822        let (representatives, term_sources, source_count) =
1823            canonical_shared_source_schedule::<N>(|term, representative| {
1824                std::ptr::eq(lefts[term], lefts[representative])
1825                    && addend_scales[term] == addend_scales[representative]
1826            });
1827        let (value, source_derivatives) =
1828            aggregate_shared_source_derivatives(&term_sources, input_scales, derivative_stacks);
1829        let source_gradients = arena.zeros(source_count * dimension);
1830        let gradient = arena.zeros(dimension);
1831        let hessian = arena.zeros(dimension * dimension);
1832        let mut right_first = 0.0;
1833        let mut addend_first = 0.0;
1834        for source in 0..source_count {
1835            let term = representatives[source];
1836            let first = source_derivatives[source][1];
1837            right_first += first * lefts[term].v;
1838            addend_first += first * addend_scales[term];
1839            for primary in 0..dimension {
1840                let product_gradient =
1841                    lefts[term].v * right.g[primary] + lefts[term].g[primary] * right.v;
1842                let inner_gradient = if addend_scales[term] == 0.0 {
1843                    product_gradient
1844                } else if addend_scales[term] == 1.0 {
1845                    product_gradient + addend.g[primary]
1846                } else {
1847                    product_gradient + addend_scales[term] * addend.g[primary]
1848                };
1849                source_gradients[source * dimension + primary] = inner_gradient;
1850                gradient[primary] += first * lefts[term].g[primary] * right.v;
1851            }
1852        }
1853        if N != 0 {
1854            for primary in 0..dimension {
1855                gradient[primary] += right_first * right.g[primary];
1856            }
1857        }
1858        if addend_live {
1859            for primary in 0..dimension {
1860                gradient[primary] += addend_first * addend.g[primary];
1861            }
1862        }
1863        for primary in 0..dimension {
1864            for other in primary..dimension {
1865                let index = primary * dimension + other;
1866                let mut channel = if N == 0 {
1867                    0.0
1868                } else {
1869                    right_first * right.h[index]
1870                };
1871                if addend_live {
1872                    channel += addend_first * addend.h[index];
1873                }
1874                for source in 0..source_count {
1875                    let term = representatives[source];
1876                    let local_product_hessian = lefts[term].g[primary] * right.g[other]
1877                        + lefts[term].g[other] * right.g[primary]
1878                        + lefts[term].h[index] * right.v;
1879                    channel += source_derivatives[source][1] * local_product_hessian
1880                        + source_derivatives[source][2]
1881                            * source_gradients[source * dimension + primary]
1882                            * source_gradients[source * dimension + other];
1883                }
1884                hessian[index] = channel;
1885                hessian[other * dimension + primary] = channel;
1886            }
1887        }
1888        Self {
1889            arena,
1890            v: value,
1891            g: gradient,
1892            h: hessian,
1893        }
1894    }
1895
1896    #[inline(always)]
1897    fn multiply_add(&self, right: &Self, addend: &Self) -> Self {
1898        self.assert_compatible(right);
1899        self.assert_compatible(addend);
1900        let dimension = self.dimension();
1901        let gradient = self.arena.zeros(dimension);
1902        let hessian = self.arena.zeros(dimension * dimension);
1903        for primary in 0..dimension {
1904            gradient[primary] =
1905                self.v * right.g[primary] + self.g[primary] * right.v + addend.g[primary];
1906            for other in primary..dimension {
1907                let index = primary * dimension + other;
1908                let channel = self.v * right.h[index]
1909                    + self.g[primary] * right.g[other]
1910                    + self.g[other] * right.g[primary]
1911                    + self.h[index] * right.v
1912                    + addend.h[index];
1913                hessian[index] = channel;
1914                hessian[other * dimension + primary] = channel;
1915            }
1916        }
1917        Self {
1918            arena: self.arena,
1919            v: self.v * right.v + addend.v,
1920            g: gradient,
1921            h: hessian,
1922        }
1923    }
1924
1925    #[inline(always)]
1926    fn composed_sum(
1927        inputs: &[Self],
1928        derivative_stacks: &[[f64; 5]],
1929        dimension: usize,
1930        arena: &'arena DynamicJetArena,
1931    ) -> Self {
1932        assert_eq!(inputs.len(), derivative_stacks.len());
1933        assert!(
1934            inputs.iter().all(|input| {
1935                input.dimension() == dimension && std::ptr::eq(input.arena, arena)
1936            }),
1937            "dynamic composed-sum jets must share dimension and arena"
1938        );
1939        let gradient = arena.zeros(dimension);
1940        let hessian = arena.zeros(dimension * dimension);
1941        let mut value = 0.0;
1942        for (input, stack) in inputs.iter().zip(derivative_stacks) {
1943            value += stack[0];
1944            for primary in 0..dimension {
1945                gradient[primary] += stack[1] * input.g[primary];
1946                for other in primary..dimension {
1947                    let index = primary * dimension + other;
1948                    hessian[index] +=
1949                        stack[1] * input.h[index] + stack[2] * input.g[primary] * input.g[other];
1950                }
1951            }
1952        }
1953        for primary in 0..dimension {
1954            for other in primary + 1..dimension {
1955                hessian[other * dimension + primary] = hessian[primary * dimension + other];
1956            }
1957        }
1958        Self {
1959            arena,
1960            v: value,
1961            g: gradient,
1962            h: hessian,
1963        }
1964    }
1965
1966    #[inline(always)]
1967    fn linear_combination(
1968        inputs: &[Self],
1969        weights: &[f64],
1970        dimension: usize,
1971        arena: &'arena DynamicJetArena,
1972    ) -> Self {
1973        assert_eq!(inputs.len(), weights.len());
1974        assert!(
1975            inputs.iter().all(|input| {
1976                input.dimension() == dimension && std::ptr::eq(input.arena, arena)
1977            }),
1978            "dynamic linear-combination jets must share dimension and arena"
1979        );
1980        let mut value = 0.0;
1981        for (input, &weight) in inputs.iter().zip(weights) {
1982            value += input.v * weight;
1983        }
1984        let gradient = arena.zeros(dimension);
1985        let hessian = arena.zeros(dimension * dimension);
1986        for primary in 0..dimension {
1987            for (input, &weight) in inputs.iter().zip(weights) {
1988                gradient[primary] += input.g[primary] * weight;
1989            }
1990            for other in primary..dimension {
1991                let index = primary * dimension + other;
1992                for (input, &weight) in inputs.iter().zip(weights) {
1993                    hessian[index] += input.h[index] * weight;
1994                }
1995                hessian[other * dimension + primary] = hessian[index];
1996            }
1997        }
1998        Self {
1999            arena,
2000            v: value,
2001            g: gradient,
2002            h: hessian,
2003        }
2004    }
2005
2006    #[inline(always)]
2007    fn dimension(&self) -> usize {
2008        self.g.len()
2009    }
2010
2011    #[inline(always)]
2012    fn value(&self) -> f64 {
2013        self.v
2014    }
2015
2016    #[inline(always)]
2017    fn add(&self, o: &Self) -> Self {
2018        self.assert_compatible(o);
2019        let dimension = self.dimension();
2020        let g = self.arena.zeros(dimension);
2021        let h = self.arena.zeros(self.h.len());
2022        for i in 0..g.len() {
2023            g[i] = self.g[i] + o.g[i];
2024        }
2025        for row in 0..dimension {
2026            for column in row..dimension {
2027                let index = row * dimension + column;
2028                let channel = self.h[index] + o.h[index];
2029                h[index] = channel;
2030                h[column * dimension + row] = channel;
2031            }
2032        }
2033        Self {
2034            arena: self.arena,
2035            v: self.v + o.v,
2036            g,
2037            h,
2038        }
2039    }
2040
2041    #[inline(always)]
2042    fn sub(&self, o: &Self) -> Self {
2043        self.assert_compatible(o);
2044        let dimension = self.dimension();
2045        let g = self.arena.zeros(dimension);
2046        let h = self.arena.zeros(self.h.len());
2047        for i in 0..g.len() {
2048            g[i] = self.g[i] - o.g[i];
2049        }
2050        for row in 0..dimension {
2051            for column in row..dimension {
2052                let index = row * dimension + column;
2053                let channel = self.h[index] - o.h[index];
2054                h[index] = channel;
2055                h[column * dimension + row] = channel;
2056            }
2057        }
2058        Self {
2059            arena: self.arena,
2060            v: self.v - o.v,
2061            g,
2062            h,
2063        }
2064    }
2065
2066    #[inline(always)]
2067    fn mul(&self, o: &Self) -> Self {
2068        self.assert_compatible(o);
2069        let n = self.dimension();
2070        let g = self.arena.zeros(n);
2071        let h = self.arena.zeros(n * n);
2072        for i in 0..n {
2073            g[i] = self.v * o.g[i] + self.g[i] * o.v;
2074        }
2075        for i in 0..n {
2076            for j in i..n {
2077                let ij = i * n + j;
2078                let hij =
2079                    self.v * o.h[ij] + self.g[i] * o.g[j] + self.g[j] * o.g[i] + self.h[ij] * o.v;
2080                h[ij] = hij;
2081                h[j * n + i] = hij;
2082            }
2083        }
2084        Self {
2085            arena: self.arena,
2086            v: self.v * o.v,
2087            g,
2088            h,
2089        }
2090    }
2091
2092    #[inline(always)]
2093    fn neg(&self) -> Self {
2094        self.scale(-1.0)
2095    }
2096
2097    #[inline(always)]
2098    fn scale(&self, s: f64) -> Self {
2099        let dimension = self.dimension();
2100        let g = self.arena.zeros(dimension);
2101        let h = self.arena.zeros(self.h.len());
2102        for i in 0..g.len() {
2103            g[i] = self.g[i] * s;
2104        }
2105        for row in 0..dimension {
2106            for column in row..dimension {
2107                let index = row * dimension + column;
2108                let channel = self.h[index] * s;
2109                h[index] = channel;
2110                h[column * dimension + row] = channel;
2111            }
2112        }
2113        Self {
2114            arena: self.arena,
2115            v: self.v * s,
2116            g,
2117            h,
2118        }
2119    }
2120
2121    #[inline(always)]
2122    fn compose_unary(&self, d: [f64; 5]) -> Self {
2123        let n = self.dimension();
2124        let g = self.arena.zeros(n);
2125        let h = self.arena.zeros(n * n);
2126        for i in 0..n {
2127            g[i] = d[1] * self.g[i];
2128        }
2129        for i in 0..n {
2130            for j in i..n {
2131                let ij = i * n + j;
2132                let channel = d[1] * self.h[ij] + d[2] * self.g[i] * self.g[j];
2133                h[ij] = channel;
2134                h[j * n + i] = channel;
2135            }
2136        }
2137        Self {
2138            arena: self.arena,
2139            v: d[0],
2140            g,
2141            h,
2142        }
2143    }
2144}
2145
2146/// Runtime-sized one-seed scalar for a Hessian-contracted third derivative.
2147#[derive(Clone, Copy, Debug)]
2148pub struct DynamicOneSeed<'arena> {
2149    /// Base value/gradient/Hessian channels.
2150    pub base: DynamicOrder2<'arena>,
2151    /// Nilpotent `epsilon` coefficient.
2152    pub eps: DynamicOrder2<'arena>,
2153}
2154
2155impl<'arena> DynamicOneSeed<'arena> {
2156    /// Seed one primary with the supplied contraction direction component.
2157    #[inline(always)]
2158    #[must_use]
2159    pub fn seed_direction(
2160        x: f64,
2161        axis: usize,
2162        u_axis: f64,
2163        dimension: usize,
2164        arena: &'arena DynamicJetArena,
2165    ) -> Self {
2166        Self {
2167            base: DynamicOrder2::variable(x, axis, dimension, arena),
2168            eps: DynamicOrder2::constant(u_axis, dimension, arena),
2169        }
2170    }
2171
2172    /// Row-major contracted-third matrix.
2173    #[inline(always)]
2174    #[must_use]
2175    pub fn contracted_third(&self) -> &[f64] {
2176        self.eps.h()
2177    }
2178}
2179
2180impl<'arena> RuntimeJetScalar<'arena> for DynamicOneSeed<'arena> {
2181    type Workspace = DynamicJetArena;
2182
2183    #[inline(always)]
2184    fn constant(c: f64, dimension: usize, arena: &'arena DynamicJetArena) -> Self {
2185        Self {
2186            base: DynamicOrder2::constant(c, dimension, arena),
2187            eps: DynamicOrder2::constant(0.0, dimension, arena),
2188        }
2189    }
2190
2191    #[inline(always)]
2192    fn variable(x: f64, axis: usize, dimension: usize, arena: &'arena DynamicJetArena) -> Self {
2193        Self {
2194            base: DynamicOrder2::variable(x, axis, dimension, arena),
2195            eps: DynamicOrder2::constant(0.0, dimension, arena),
2196        }
2197    }
2198
2199    #[inline(always)]
2200    fn constant_like(&self, c: f64) -> Self {
2201        Self {
2202            base: self.base.constant_like(c),
2203            eps: self.eps.constant_like(0.0),
2204        }
2205    }
2206
2207    #[inline(always)]
2208    fn with_value(&self, value: f64) -> Self {
2209        Self {
2210            base: self.base.with_value(value),
2211            eps: self.eps,
2212        }
2213    }
2214
2215    #[inline(always)]
2216    fn dimension(&self) -> usize {
2217        self.base.dimension()
2218    }
2219
2220    #[inline(always)]
2221    fn value(&self) -> f64 {
2222        self.base.value()
2223    }
2224
2225    #[inline(always)]
2226    fn add(&self, o: &Self) -> Self {
2227        Self {
2228            base: self.base.add(&o.base),
2229            eps: self.eps.add(&o.eps),
2230        }
2231    }
2232
2233    #[inline(always)]
2234    fn sub(&self, o: &Self) -> Self {
2235        Self {
2236            base: self.base.sub(&o.base),
2237            eps: self.eps.sub(&o.eps),
2238        }
2239    }
2240
2241    #[inline(always)]
2242    fn mul(&self, o: &Self) -> Self {
2243        self.base.assert_compatible(&o.base);
2244        self.eps.assert_compatible(&o.eps);
2245        Self {
2246            base: self.base.mul(&o.base),
2247            eps: DynamicOrder2::from_channel_functions(
2248                self.base.v * o.eps.v + self.eps.v * o.base.v,
2249                self.dimension(),
2250                self.base.arena,
2251                |i| {
2252                    self.base.v * o.eps.g[i]
2253                        + self.base.g[i] * o.eps.v
2254                        + self.eps.v * o.base.g[i]
2255                        + self.eps.g[i] * o.base.v
2256                },
2257                |i, j| {
2258                    let ij = i * self.dimension() + j;
2259                    self.base.v * o.eps.h[ij]
2260                        + self.base.g[i] * o.eps.g[j]
2261                        + self.base.g[j] * o.eps.g[i]
2262                        + self.base.h[ij] * o.eps.v
2263                        + self.eps.v * o.base.h[ij]
2264                        + self.eps.g[i] * o.base.g[j]
2265                        + self.eps.g[j] * o.base.g[i]
2266                        + self.eps.h[ij] * o.base.v
2267                },
2268            ),
2269        }
2270    }
2271
2272    #[inline(always)]
2273    fn neg(&self) -> Self {
2274        Self {
2275            base: self.base.neg(),
2276            eps: self.eps.neg(),
2277        }
2278    }
2279
2280    #[inline(always)]
2281    fn scale(&self, s: f64) -> Self {
2282        Self {
2283            base: self.base.scale(s),
2284            eps: self.eps.scale(s),
2285        }
2286    }
2287
2288    #[inline(always)]
2289    fn compose_unary(&self, d: [f64; 5]) -> Self {
2290        let base = self.base.compose_unary(d);
2291        let dimension = self.dimension();
2292        let eps = DynamicOrder2::from_channel_functions(
2293            d[1] * self.eps.v,
2294            dimension,
2295            self.base.arena,
2296            |i| d[2] * self.base.g[i] * self.eps.v + d[1] * self.eps.g[i],
2297            |i, j| {
2298                let ij = i * dimension + j;
2299                d[1] * self.eps.h[ij]
2300                    + d[2]
2301                        * (self.base.g[i] * self.eps.g[j]
2302                            + self.base.g[j] * self.eps.g[i]
2303                            + self.base.h[ij] * self.eps.v)
2304                    + d[3] * self.base.g[i] * self.base.g[j] * self.eps.v
2305            },
2306        );
2307        Self { base, eps }
2308    }
2309}
2310
2311/// Reusable arena plus lane count for runtime directional batches.
2312///
2313/// A caller resets this once per row/chunk and evaluates every requested
2314/// third- or fourth-derivative contraction in one row-program pass. The bump
2315/// retains its largest chunk, so warmed rows do not return to the global
2316/// allocator.
2317#[derive(Debug)]
2318pub struct DynamicJetBatchWorkspace {
2319    arena: DynamicJetArena,
2320    lanes: usize,
2321}
2322
2323impl DynamicJetBatchWorkspace {
2324    /// Create a reusable workspace for `lanes` simultaneous directions.
2325    #[must_use]
2326    pub fn new(lanes: usize) -> Self {
2327        Self {
2328            arena: DynamicJetArena::new(),
2329            lanes,
2330        }
2331    }
2332
2333    /// Reclaim all jet storage and select the next evaluation's lane count.
2334    pub fn reset(&mut self, lanes: usize) {
2335        self.arena.reset();
2336        self.lanes = lanes;
2337    }
2338
2339    /// Bytes retained by the bump allocator after the largest evaluation.
2340    #[must_use]
2341    pub fn allocated_bytes(&self) -> usize {
2342        self.arena.allocated_bytes()
2343    }
2344
2345    /// Allocate a primary array in the same arena as every scalar channel.
2346    #[inline(always)]
2347    pub fn alloc_slice_fill_with<T>(&self, len: usize, fill: impl FnMut(usize) -> T) -> &mut [T] {
2348        self.arena.alloc_slice_fill_with(len, fill)
2349    }
2350}
2351
2352/// Runtime-sized batch of one-seed contractions sharing one order-two base.
2353///
2354/// Lane `l` is algebraically identical to a standalone [`DynamicOneSeed`]
2355/// seeded by direction `u_l`, but the value/gradient/Hessian base is evaluated
2356/// once rather than once per direction. The output lane's epsilon Hessian is
2357/// `sum_c d³f/(dx_a dx_b dx_c) u_l[c]`.
2358#[derive(Clone, Copy, Debug)]
2359pub struct DynamicOneSeedBatch<'arena> {
2360    /// Shared value/gradient/Hessian channels.
2361    pub base: DynamicOrder2<'arena>,
2362    /// One nilpotent epsilon coefficient per contraction direction.
2363    eps: &'arena [DynamicOrder2<'arena>],
2364}
2365
2366impl<'arena> DynamicOneSeedBatch<'arena> {
2367    /// Seed one primary across every direction lane.
2368    #[inline(always)]
2369    #[must_use]
2370    pub fn seed_directions(
2371        x: f64,
2372        axis: usize,
2373        dimension: usize,
2374        workspace: &'arena DynamicJetBatchWorkspace,
2375        mut direction_at: impl FnMut(usize) -> f64,
2376    ) -> Self {
2377        let eps = workspace
2378            .arena
2379            .alloc_slice_fill_with(workspace.lanes, |lane| {
2380                DynamicOrder2::constant(direction_at(lane), dimension, &workspace.arena)
2381            });
2382        Self {
2383            base: DynamicOrder2::variable(x, axis, dimension, &workspace.arena),
2384            eps,
2385        }
2386    }
2387
2388    /// Number of simultaneous contraction directions.
2389    #[inline(always)]
2390    #[must_use]
2391    pub fn lanes(&self) -> usize {
2392        self.eps.len()
2393    }
2394
2395    /// Row-major contracted-third matrix for one direction lane.
2396    #[inline(always)]
2397    #[must_use]
2398    pub fn contracted_third(&self, lane: usize) -> &[f64] {
2399        self.eps[lane].h()
2400    }
2401
2402    #[inline(always)]
2403    fn assert_compatible(&self, other: &Self) {
2404        self.base.assert_compatible(&other.base);
2405        assert_eq!(
2406            self.eps.len(),
2407            other.eps.len(),
2408            "dynamic one-seed batch lane mismatch"
2409        );
2410    }
2411}
2412
2413impl<'arena> RuntimeJetScalar<'arena> for DynamicOneSeedBatch<'arena> {
2414    type Workspace = DynamicJetBatchWorkspace;
2415
2416    #[inline(always)]
2417    fn constant(c: f64, dimension: usize, workspace: &'arena DynamicJetBatchWorkspace) -> Self {
2418        let eps = workspace.arena.alloc_slice_fill_with(workspace.lanes, |_| {
2419            DynamicOrder2::constant(0.0, dimension, &workspace.arena)
2420        });
2421        Self {
2422            base: DynamicOrder2::constant(c, dimension, &workspace.arena),
2423            eps,
2424        }
2425    }
2426
2427    #[inline(always)]
2428    fn variable(
2429        x: f64,
2430        axis: usize,
2431        dimension: usize,
2432        workspace: &'arena DynamicJetBatchWorkspace,
2433    ) -> Self {
2434        let eps = workspace.arena.alloc_slice_fill_with(workspace.lanes, |_| {
2435            DynamicOrder2::constant(0.0, dimension, &workspace.arena)
2436        });
2437        Self {
2438            base: DynamicOrder2::variable(x, axis, dimension, &workspace.arena),
2439            eps,
2440        }
2441    }
2442
2443    #[inline(always)]
2444    fn constant_like(&self, c: f64) -> Self {
2445        let eps = self
2446            .base
2447            .arena
2448            .alloc_slice_fill_with(self.lanes(), |_| self.base.constant_like(0.0));
2449        Self {
2450            base: self.base.constant_like(c),
2451            eps,
2452        }
2453    }
2454
2455    #[inline(always)]
2456    fn with_value(&self, value: f64) -> Self {
2457        Self {
2458            base: self.base.with_value(value),
2459            eps: self.eps,
2460        }
2461    }
2462
2463    #[inline(always)]
2464    fn dimension(&self) -> usize {
2465        self.base.dimension()
2466    }
2467
2468    #[inline(always)]
2469    fn value(&self) -> f64 {
2470        self.base.value()
2471    }
2472
2473    #[inline(always)]
2474    fn add(&self, other: &Self) -> Self {
2475        self.assert_compatible(other);
2476        let eps = self
2477            .base
2478            .arena
2479            .alloc_slice_fill_with(self.eps.len(), |lane| self.eps[lane].add(&other.eps[lane]));
2480        Self {
2481            base: self.base.add(&other.base),
2482            eps,
2483        }
2484    }
2485
2486    #[inline(always)]
2487    fn sub(&self, other: &Self) -> Self {
2488        self.assert_compatible(other);
2489        let eps = self
2490            .base
2491            .arena
2492            .alloc_slice_fill_with(self.eps.len(), |lane| self.eps[lane].sub(&other.eps[lane]));
2493        Self {
2494            base: self.base.sub(&other.base),
2495            eps,
2496        }
2497    }
2498
2499    #[inline(always)]
2500    fn mul(&self, other: &Self) -> Self {
2501        self.assert_compatible(other);
2502        let eps = self
2503            .base
2504            .arena
2505            .alloc_slice_fill_with(self.eps.len(), |lane| {
2506                self.base
2507                    .mul(&other.eps[lane])
2508                    .add(&self.eps[lane].mul(&other.base))
2509            });
2510        Self {
2511            base: self.base.mul(&other.base),
2512            eps,
2513        }
2514    }
2515
2516    #[inline(always)]
2517    fn neg(&self) -> Self {
2518        self.scale(-1.0)
2519    }
2520
2521    #[inline(always)]
2522    fn scale(&self, scale: f64) -> Self {
2523        let eps = self
2524            .base
2525            .arena
2526            .alloc_slice_fill_with(self.eps.len(), |lane| self.eps[lane].scale(scale));
2527        Self {
2528            base: self.base.scale(scale),
2529            eps,
2530        }
2531    }
2532
2533    #[inline(always)]
2534    fn compose_unary(&self, derivatives: [f64; 5]) -> Self {
2535        let fprime = self.base.compose_unary([
2536            derivatives[1],
2537            derivatives[2],
2538            derivatives[3],
2539            derivatives[4],
2540            derivatives[4],
2541        ]);
2542        let eps = self
2543            .base
2544            .arena
2545            .alloc_slice_fill_with(self.eps.len(), |lane| fprime.mul(&self.eps[lane]));
2546        Self {
2547            base: self.base.compose_unary(derivatives),
2548            eps,
2549        }
2550    }
2551}
2552
2553/// Runtime-sized batch of two-seed contractions sharing one order-two base.
2554///
2555/// Lane `l` is algebraically identical to a standalone [`DynamicTwoSeed`]
2556/// seeded by the direction pair `(u_l, v_l)`, while the base row expression is
2557/// traversed once for the entire pair batch. Storage is `O(lanes * K^2)`; no
2558/// dense fourth-order tensor is formed.
2559#[derive(Clone, Copy, Debug)]
2560pub struct DynamicTwoSeedBatch<'arena> {
2561    /// Shared value/gradient/Hessian channels.
2562    pub base: DynamicOrder2<'arena>,
2563    eps: &'arena [DynamicOrder2<'arena>],
2564    del: &'arena [DynamicOrder2<'arena>],
2565    eps_del: &'arena [DynamicOrder2<'arena>],
2566}
2567
2568impl<'arena> DynamicTwoSeedBatch<'arena> {
2569    /// Seed one primary across every direction-pair lane.
2570    #[inline(always)]
2571    #[must_use]
2572    pub fn seed_direction_pairs(
2573        x: f64,
2574        axis: usize,
2575        dimension: usize,
2576        workspace: &'arena DynamicJetBatchWorkspace,
2577        mut direction_pair_at: impl FnMut(usize) -> (f64, f64),
2578    ) -> Self {
2579        let directions = workspace
2580            .arena
2581            .alloc_slice_fill_with(workspace.lanes, |lane| direction_pair_at(lane));
2582        let eps = workspace
2583            .arena
2584            .alloc_slice_fill_with(workspace.lanes, |lane| {
2585                DynamicOrder2::constant(directions[lane].0, dimension, &workspace.arena)
2586            });
2587        let del = workspace
2588            .arena
2589            .alloc_slice_fill_with(workspace.lanes, |lane| {
2590                DynamicOrder2::constant(directions[lane].1, dimension, &workspace.arena)
2591            });
2592        let eps_del = workspace.arena.alloc_slice_fill_with(workspace.lanes, |_| {
2593            DynamicOrder2::constant(0.0, dimension, &workspace.arena)
2594        });
2595        Self {
2596            base: DynamicOrder2::variable(x, axis, dimension, &workspace.arena),
2597            eps,
2598            del,
2599            eps_del,
2600        }
2601    }
2602
2603    /// Number of simultaneous contraction pairs.
2604    #[inline(always)]
2605    #[must_use]
2606    pub fn lanes(&self) -> usize {
2607        self.eps.len()
2608    }
2609
2610    /// Row-major contracted-fourth matrix for one direction-pair lane.
2611    #[inline(always)]
2612    #[must_use]
2613    pub fn contracted_fourth(&self, lane: usize) -> &[f64] {
2614        self.eps_del[lane].h()
2615    }
2616
2617    #[inline(always)]
2618    fn assert_compatible(&self, other: &Self) {
2619        self.base.assert_compatible(&other.base);
2620        assert_eq!(
2621            self.eps.len(),
2622            other.eps.len(),
2623            "dynamic two-seed batch lane mismatch"
2624        );
2625        assert_eq!(
2626            self.del.len(),
2627            self.eps.len(),
2628            "dynamic two-seed batch delta mismatch"
2629        );
2630        assert_eq!(
2631            self.eps_del.len(),
2632            self.eps.len(),
2633            "dynamic two-seed batch cross mismatch"
2634        );
2635    }
2636}
2637
2638impl<'arena> RuntimeJetScalar<'arena> for DynamicTwoSeedBatch<'arena> {
2639    type Workspace = DynamicJetBatchWorkspace;
2640
2641    #[inline(always)]
2642    fn constant(c: f64, dimension: usize, workspace: &'arena Self::Workspace) -> Self {
2643        let zero = workspace.arena.alloc_slice_fill_with(workspace.lanes, |_| {
2644            DynamicOrder2::constant(0.0, dimension, &workspace.arena)
2645        });
2646        Self {
2647            base: DynamicOrder2::constant(c, dimension, &workspace.arena),
2648            eps: zero,
2649            del: zero,
2650            eps_del: zero,
2651        }
2652    }
2653
2654    #[inline(always)]
2655    fn variable(x: f64, axis: usize, dimension: usize, workspace: &'arena Self::Workspace) -> Self {
2656        let zero = workspace.arena.alloc_slice_fill_with(workspace.lanes, |_| {
2657            DynamicOrder2::constant(0.0, dimension, &workspace.arena)
2658        });
2659        Self {
2660            base: DynamicOrder2::variable(x, axis, dimension, &workspace.arena),
2661            eps: zero,
2662            del: zero,
2663            eps_del: zero,
2664        }
2665    }
2666
2667    #[inline(always)]
2668    fn constant_like(&self, c: f64) -> Self {
2669        let zero = self
2670            .base
2671            .arena
2672            .alloc_slice_fill_with(self.lanes(), |_| self.base.constant_like(0.0));
2673        Self {
2674            base: self.base.constant_like(c),
2675            eps: zero,
2676            del: zero,
2677            eps_del: zero,
2678        }
2679    }
2680
2681    #[inline(always)]
2682    fn with_value(&self, value: f64) -> Self {
2683        Self {
2684            base: self.base.with_value(value),
2685            eps: self.eps,
2686            del: self.del,
2687            eps_del: self.eps_del,
2688        }
2689    }
2690
2691    #[inline(always)]
2692    fn dimension(&self) -> usize {
2693        self.base.dimension()
2694    }
2695
2696    #[inline(always)]
2697    fn value(&self) -> f64 {
2698        self.base.value()
2699    }
2700
2701    #[inline(always)]
2702    fn add(&self, other: &Self) -> Self {
2703        self.assert_compatible(other);
2704        let arena = self.base.arena;
2705        let eps =
2706            arena.alloc_slice_fill_with(self.lanes(), |lane| self.eps[lane].add(&other.eps[lane]));
2707        let del =
2708            arena.alloc_slice_fill_with(self.lanes(), |lane| self.del[lane].add(&other.del[lane]));
2709        let eps_del = arena.alloc_slice_fill_with(self.lanes(), |lane| {
2710            self.eps_del[lane].add(&other.eps_del[lane])
2711        });
2712        Self {
2713            base: self.base.add(&other.base),
2714            eps,
2715            del,
2716            eps_del,
2717        }
2718    }
2719
2720    #[inline(always)]
2721    fn sub(&self, other: &Self) -> Self {
2722        self.assert_compatible(other);
2723        let arena = self.base.arena;
2724        let eps =
2725            arena.alloc_slice_fill_with(self.lanes(), |lane| self.eps[lane].sub(&other.eps[lane]));
2726        let del =
2727            arena.alloc_slice_fill_with(self.lanes(), |lane| self.del[lane].sub(&other.del[lane]));
2728        let eps_del = arena.alloc_slice_fill_with(self.lanes(), |lane| {
2729            self.eps_del[lane].sub(&other.eps_del[lane])
2730        });
2731        Self {
2732            base: self.base.sub(&other.base),
2733            eps,
2734            del,
2735            eps_del,
2736        }
2737    }
2738
2739    #[inline(always)]
2740    fn mul(&self, other: &Self) -> Self {
2741        self.assert_compatible(other);
2742        let arena = self.base.arena;
2743        let eps = arena.alloc_slice_fill_with(self.lanes(), |lane| {
2744            self.base
2745                .mul(&other.eps[lane])
2746                .add(&self.eps[lane].mul(&other.base))
2747        });
2748        let del = arena.alloc_slice_fill_with(self.lanes(), |lane| {
2749            self.base
2750                .mul(&other.del[lane])
2751                .add(&self.del[lane].mul(&other.base))
2752        });
2753        let eps_del = arena.alloc_slice_fill_with(self.lanes(), |lane| {
2754            self.base
2755                .mul(&other.eps_del[lane])
2756                .add(&self.eps[lane].mul(&other.del[lane]))
2757                .add(&self.del[lane].mul(&other.eps[lane]))
2758                .add(&self.eps_del[lane].mul(&other.base))
2759        });
2760        Self {
2761            base: self.base.mul(&other.base),
2762            eps,
2763            del,
2764            eps_del,
2765        }
2766    }
2767
2768    #[inline(always)]
2769    fn neg(&self) -> Self {
2770        self.scale(-1.0)
2771    }
2772
2773    #[inline(always)]
2774    fn scale(&self, scale: f64) -> Self {
2775        let arena = self.base.arena;
2776        let eps = arena.alloc_slice_fill_with(self.lanes(), |lane| self.eps[lane].scale(scale));
2777        let del = arena.alloc_slice_fill_with(self.lanes(), |lane| self.del[lane].scale(scale));
2778        let eps_del =
2779            arena.alloc_slice_fill_with(self.lanes(), |lane| self.eps_del[lane].scale(scale));
2780        Self {
2781            base: self.base.scale(scale),
2782            eps,
2783            del,
2784            eps_del,
2785        }
2786    }
2787
2788    #[inline(always)]
2789    fn compose_unary(&self, derivatives: [f64; 5]) -> Self {
2790        let arena = self.base.arena;
2791        let fprime = self.base.compose_unary([
2792            derivatives[1],
2793            derivatives[2],
2794            derivatives[3],
2795            derivatives[4],
2796            derivatives[4],
2797        ]);
2798        let fsecond = self.base.compose_unary([
2799            derivatives[2],
2800            derivatives[3],
2801            derivatives[4],
2802            derivatives[4],
2803            derivatives[4],
2804        ]);
2805        let eps = arena.alloc_slice_fill_with(self.lanes(), |lane| fprime.mul(&self.eps[lane]));
2806        let del = arena.alloc_slice_fill_with(self.lanes(), |lane| fprime.mul(&self.del[lane]));
2807        let eps_del = arena.alloc_slice_fill_with(self.lanes(), |lane| {
2808            fsecond
2809                .mul(&self.eps[lane])
2810                .mul(&self.del[lane])
2811                .add(&fprime.mul(&self.eps_del[lane]))
2812        });
2813        Self {
2814            base: self.base.compose_unary(derivatives),
2815            eps,
2816            del,
2817            eps_del,
2818        }
2819    }
2820}
2821
2822/// Runtime-sized two-seed scalar for a Hessian-contracted fourth derivative.
2823#[derive(Clone, Copy, Debug)]
2824pub struct DynamicTwoSeed<'arena> {
2825    /// Base value/gradient/Hessian channels.
2826    pub base: DynamicOrder2<'arena>,
2827    /// Nilpotent `epsilon` coefficient.
2828    pub eps: DynamicOrder2<'arena>,
2829    /// Nilpotent `delta` coefficient.
2830    pub del: DynamicOrder2<'arena>,
2831    /// Mixed `epsilon delta` coefficient.
2832    pub eps_del: DynamicOrder2<'arena>,
2833}
2834
2835impl<'arena> DynamicTwoSeed<'arena> {
2836    /// Seed one primary with both contraction direction components.
2837    #[inline(always)]
2838    #[must_use]
2839    pub fn seed(
2840        x: f64,
2841        axis: usize,
2842        u_axis: f64,
2843        v_axis: f64,
2844        dimension: usize,
2845        arena: &'arena DynamicJetArena,
2846    ) -> Self {
2847        Self {
2848            base: DynamicOrder2::variable(x, axis, dimension, arena),
2849            eps: DynamicOrder2::constant(u_axis, dimension, arena),
2850            del: DynamicOrder2::constant(v_axis, dimension, arena),
2851            eps_del: DynamicOrder2::constant(0.0, dimension, arena),
2852        }
2853    }
2854
2855    /// Row-major contracted-fourth matrix.
2856    #[inline(always)]
2857    #[must_use]
2858    pub fn contracted_fourth(&self) -> &[f64] {
2859        self.eps_del.h()
2860    }
2861}
2862
2863impl<'arena> RuntimeJetScalar<'arena> for DynamicTwoSeed<'arena> {
2864    type Workspace = DynamicJetArena;
2865
2866    #[inline(always)]
2867    fn constant(c: f64, dimension: usize, arena: &'arena DynamicJetArena) -> Self {
2868        Self {
2869            base: DynamicOrder2::constant(c, dimension, arena),
2870            eps: DynamicOrder2::constant(0.0, dimension, arena),
2871            del: DynamicOrder2::constant(0.0, dimension, arena),
2872            eps_del: DynamicOrder2::constant(0.0, dimension, arena),
2873        }
2874    }
2875
2876    #[inline(always)]
2877    fn variable(x: f64, axis: usize, dimension: usize, arena: &'arena DynamicJetArena) -> Self {
2878        Self {
2879            base: DynamicOrder2::variable(x, axis, dimension, arena),
2880            eps: DynamicOrder2::constant(0.0, dimension, arena),
2881            del: DynamicOrder2::constant(0.0, dimension, arena),
2882            eps_del: DynamicOrder2::constant(0.0, dimension, arena),
2883        }
2884    }
2885
2886    #[inline(always)]
2887    fn constant_like(&self, c: f64) -> Self {
2888        Self {
2889            base: self.base.constant_like(c),
2890            eps: self.eps.constant_like(0.0),
2891            del: self.del.constant_like(0.0),
2892            eps_del: self.eps_del.constant_like(0.0),
2893        }
2894    }
2895
2896    #[inline(always)]
2897    fn with_value(&self, value: f64) -> Self {
2898        Self {
2899            base: self.base.with_value(value),
2900            eps: self.eps,
2901            del: self.del,
2902            eps_del: self.eps_del,
2903        }
2904    }
2905
2906    #[inline(always)]
2907    fn dimension(&self) -> usize {
2908        self.base.dimension()
2909    }
2910
2911    #[inline(always)]
2912    fn value(&self) -> f64 {
2913        self.base.value()
2914    }
2915
2916    #[inline(always)]
2917    fn add(&self, o: &Self) -> Self {
2918        Self {
2919            base: self.base.add(&o.base),
2920            eps: self.eps.add(&o.eps),
2921            del: self.del.add(&o.del),
2922            eps_del: self.eps_del.add(&o.eps_del),
2923        }
2924    }
2925
2926    #[inline(always)]
2927    fn sub(&self, o: &Self) -> Self {
2928        Self {
2929            base: self.base.sub(&o.base),
2930            eps: self.eps.sub(&o.eps),
2931            del: self.del.sub(&o.del),
2932            eps_del: self.eps_del.sub(&o.eps_del),
2933        }
2934    }
2935
2936    #[inline(always)]
2937    fn mul(&self, o: &Self) -> Self {
2938        let base = self.base.mul(&o.base);
2939        let eps = self.base.mul(&o.eps).add(&self.eps.mul(&o.base));
2940        let del = self.base.mul(&o.del).add(&self.del.mul(&o.base));
2941        let eps_del = self
2942            .base
2943            .mul(&o.eps_del)
2944            .add(&self.eps.mul(&o.del))
2945            .add(&self.del.mul(&o.eps))
2946            .add(&self.eps_del.mul(&o.base));
2947        Self {
2948            base,
2949            eps,
2950            del,
2951            eps_del,
2952        }
2953    }
2954
2955    #[inline(always)]
2956    fn neg(&self) -> Self {
2957        Self {
2958            base: self.base.neg(),
2959            eps: self.eps.neg(),
2960            del: self.del.neg(),
2961            eps_del: self.eps_del.neg(),
2962        }
2963    }
2964
2965    #[inline(always)]
2966    fn scale(&self, s: f64) -> Self {
2967        Self {
2968            base: self.base.scale(s),
2969            eps: self.eps.scale(s),
2970            del: self.del.scale(s),
2971            eps_del: self.eps_del.scale(s),
2972        }
2973    }
2974
2975    #[inline(always)]
2976    fn compose_unary(&self, d: [f64; 5]) -> Self {
2977        let base = self.base.compose_unary(d);
2978        let fprime = self.base.compose_unary([d[1], d[2], d[3], d[4], d[4]]);
2979        let fsecond = self.base.compose_unary([d[2], d[3], d[4], d[4], d[4]]);
2980        let eps = fprime.mul(&self.eps);
2981        let del = fprime.mul(&self.del);
2982        let eps_del = fsecond
2983            .mul(&self.eps)
2984            .mul(&self.del)
2985            .add(&fprime.mul(&self.eps_del));
2986        Self {
2987            base,
2988            eps,
2989            del,
2990            eps_del,
2991        }
2992    }
2993}
2994
2995// ── Order2<K> ergonomic operator overloads (doc §A.1) ───────────────────
2996//
2997// The dispersion-family row NLLs are written with `+`/`-`/`*` operators over
2998// the primaries (mirroring how they read as `Tower4` expressions). These
2999// delegate channel-for-channel to the inner `Tower2` arithmetic (which has
3000// `Add`/`Mul`; `Sub`/`Neg` are expressed as `+ (-1)·rhs` exactly as the
3001// `crate::nested_dual::JetField::sub` / `crate::nested_dual::JetField::neg` impls do), so an `Order2` expression is
3002// bit-identical to the same `Tower4` expression's order-≤2 channels.
3003
3004impl<const K: usize> std::ops::Add for Order2<K> {
3005    type Output = Self;
3006    #[inline]
3007    fn add(self, o: Self) -> Self {
3008        Order2(self.0 + o.0)
3009    }
3010}
3011
3012impl<const K: usize> std::ops::Add<f64> for Order2<K> {
3013    type Output = Self;
3014    #[inline]
3015    fn add(self, c: f64) -> Self {
3016        Order2(self.0 + c)
3017    }
3018}
3019
3020impl<const K: usize> std::ops::Sub for Order2<K> {
3021    type Output = Self;
3022    #[inline]
3023    fn sub(self, o: Self) -> Self {
3024        Order2(self.0 + o.0.scale(-1.0))
3025    }
3026}
3027
3028impl<const K: usize> std::ops::Sub<f64> for Order2<K> {
3029    type Output = Self;
3030    #[inline]
3031    fn sub(self, c: f64) -> Self {
3032        Order2(self.0 + (-c))
3033    }
3034}
3035
3036impl<const K: usize> std::ops::Mul for Order2<K> {
3037    type Output = Self;
3038    #[inline]
3039    fn mul(self, o: Self) -> Self {
3040        Order2(crate::jet_tower::Tower2::mul(&self.0, &o.0))
3041    }
3042}
3043
3044impl<const K: usize> std::ops::Mul<f64> for Order2<K> {
3045    type Output = Self;
3046    #[inline]
3047    fn mul(self, c: f64) -> Self {
3048        Order2(self.0.scale(c))
3049    }
3050}
3051
3052impl<const K: usize> std::ops::Neg for Order2<K> {
3053    type Output = Self;
3054    #[inline]
3055    fn neg(self) -> Self {
3056        Order2(self.0.scale(-1.0))
3057    }
3058}
3059
3060/// Filtered Hensel lift of a SCALAR implicit state `a(θ)` defined by the
3061/// constraint `F(a, θ) = 0`, evaluated in ANY [`JetScalar`] algebra `S` (doc
3062/// §11, "A generic implicit-lift operator for every production scalar").
3063///
3064/// This is the perf-respecting alternative to lifting through a dense
3065/// `Tower4<K+1>` (which carries the implicit variable as an extra dense axis):
3066/// the state `a` lives directly in the consumer's own `K`-primary algebra
3067/// `S` — `Order2<K>` for value/gradient/Hessian, `Tower4<K>` for the full
3068/// `t3`/`t4` — never paying for an extra variable.
3069///
3070/// **Method.** Fixed-Jacobian Newton in the nilpotent algebra. By the
3071/// filtered-lift theorem (doc §11.1), if `F_a := ∂F/∂a(a₀, θ₀)` is the primal
3072/// Jacobian at the base point and `inv_fa = 1/F_a`, then the iteration
3073/// `A ← A − inv_fa · F(A, θ)` raises the filtration degree of the residual by
3074/// at least one per step: each step kills exactly one graded layer. Starting
3075/// from `A = const(a₀)` (whose residual lies in `F¹` because `θ − θ₀ ∈ 𝔫`),
3076/// `iters` equal to the algebra's nilpotency order returns the *exact* lifted
3077/// jet (`Order2`: 2, `OneSeed`: 3, `Tower4`/`TwoSeed`: 4). The value channel of
3078/// `A` never moves — `F(A, θ).value() = F(a₀, θ₀) = 0` at the certified root —
3079/// so a caller may precompute every primitive's derivative stack at the fixed
3080/// base index once and let the cheap polynomial composition repeat per step.
3081///
3082/// `f` evaluates the constraint `F(a, θ)` in `S` (capturing the seeded
3083/// parameter jets `θ`); `a0` is the certified scalar root `F(a₀, θ₀) ≈ 0`.
3084pub fn filtered_implicit_solve_scalar<const K: usize, S: JetScalar<K>>(
3085    a0: f64,
3086    inv_fa: f64,
3087    iters: usize,
3088    f: impl Fn(&S) -> S,
3089) -> S {
3090    let mut a = S::constant(a0);
3091    for _ in 0..iters {
3092        let residual = f(&a);
3093        a = a.sub(&residual.scale(inv_fa));
3094    }
3095    a
3096}
3097
3098// ── PatternedOrder2<P, K, H>: compile-time sparse Hessian ───────────────
3099
3100/// Runtime-dimension form of [`filtered_implicit_solve_scalar`].
3101///
3102/// The state is lifted directly in the caller's [`RuntimeJetScalar`] algebra,
3103/// so one row expression can serve a runtime-sized value/gradient/Hessian
3104/// evaluation (`DynamicOrder2`), a Hessian-contracted third derivative
3105/// (`DynamicOneSeed`), and a Hessian-contracted fourth derivative
3106/// (`DynamicTwoSeed`). `iters` is the algebra's nilpotency order: two, three,
3107/// and four respectively. No implicit axis is appended to the derivative
3108/// storage.
3109pub fn filtered_implicit_solve_runtime_scalar<'arena, S: RuntimeJetScalar<'arena>>(
3110    a0: f64,
3111    inv_fa: f64,
3112    iters: usize,
3113    dimension: usize,
3114    workspace: &'arena S::Workspace,
3115    f: impl Fn(&S) -> S,
3116) -> S {
3117    let mut a = S::constant(a0, dimension, workspace);
3118    for _ in 0..iters {
3119        let residual = f(&a);
3120        a = a.sub(&residual.scale(inv_fa));
3121    }
3122    a
3123}
3124
3125/// Compile-time upper-triangle Hessian pattern for [`PatternedOrder2`].
3126///
3127/// `PAIRS` contains exactly the `(row, column)` channels a row program can
3128/// produce, with `row <= column`.  Pointwise Taylor algebra never couples one
3129/// Hessian pair through another: output `H[i,j]` depends only on the two input
3130/// `H[i,j]` channels and their `g[i]`/`g[j]` channels.  It is therefore exact to
3131/// omit structurally impossible pairs rather than multiplying their zeros.
3132pub trait HessianPattern<const K: usize, const H: usize> {
3133    const PAIRS: [(usize, usize); H];
3134    const PAIR_BITS: [[u128; K]; K];
3135}
3136
3137/// Build the symmetric axis-pair → patterned-slot lookup used by dependency
3138/// propagation in [`PatternedOrder2`].
3139pub const fn hessian_pair_bits<const K: usize, const H: usize>(
3140    pairs: [(usize, usize); H],
3141) -> [[u128; K]; K] {
3142    let mut table = [[0u128; K]; K];
3143    let mut slot = 0;
3144    while slot < H {
3145        let (i, j) = pairs[slot];
3146        let bit = 1u128 << slot;
3147        table[i][j] = bit;
3148        table[j][i] = bit;
3149        slot += 1;
3150    }
3151    table
3152}
3153
3154/// Exact order-two jet with a dense gradient and a compile-time patterned
3155/// upper-triangle Hessian.
3156///
3157/// This carries `1 + K + H` scalars instead of `1 + K + K²`.  Its arithmetic is
3158/// the same Leibniz/Faà-di-Bruno algebra as [`Order2`], evaluated only for the
3159/// Hessian pairs declared by `P`.  A family row NLL remains written once over
3160/// [`JetScalar`]; the pattern is an execution schedule, not a second derivative
3161/// formula.
3162#[derive(Debug)]
3163pub struct PatternedOrder2<P, const K: usize, const H: usize> {
3164    v: f64,
3165    g: [f64; K],
3166    h: [f64; H],
3167    gradient_mask: u128,
3168    hessian_mask: u128,
3169    pattern: std::marker::PhantomData<fn() -> P>,
3170}
3171
3172impl<P, const K: usize, const H: usize> Copy for PatternedOrder2<P, K, H> {}
3173
3174impl<P, const K: usize, const H: usize> Clone for PatternedOrder2<P, K, H> {
3175    fn clone(&self) -> Self {
3176        *self
3177    }
3178}
3179
3180impl<P, const K: usize, const H: usize> PatternedOrder2<P, K, H>
3181where
3182    P: HessianPattern<K, H>,
3183{
3184    #[inline]
3185    #[must_use]
3186    pub fn g(&self) -> [f64; K] {
3187        self.g
3188    }
3189
3190    /// Expand the patterned upper triangle into the dense symmetric shape
3191    /// required by the existing `RowKernel` interface. Missing pairs are exact
3192    /// structural zeros.
3193    #[inline]
3194    #[must_use]
3195    pub fn h(&self) -> [[f64; K]; K] {
3196        let mut dense = [[0.0; K]; K];
3197        for (slot, &(i, j)) in P::PAIRS.iter().enumerate() {
3198            dense[i][j] = self.h[slot];
3199            dense[j][i] = self.h[slot];
3200        }
3201        dense
3202    }
3203
3204    #[inline]
3205    fn pair_mask_between(left: u128, right: u128) -> u128 {
3206        let mut result = 0u128;
3207        let mut left_axes = left;
3208        while left_axes != 0 {
3209            let i = left_axes.trailing_zeros() as usize;
3210            left_axes &= left_axes - 1;
3211            let mut right_axes = right;
3212            while right_axes != 0 {
3213                let j = right_axes.trailing_zeros() as usize;
3214                right_axes &= right_axes - 1;
3215                result |= P::PAIR_BITS[i][j];
3216            }
3217        }
3218        result
3219    }
3220}
3221
3222impl<P, const K: usize, const H: usize> JetScalar<K> for PatternedOrder2<P, K, H>
3223where
3224    P: HessianPattern<K, H>,
3225{
3226    #[inline]
3227    fn constant(c: f64) -> Self {
3228        Self {
3229            v: c,
3230            g: [0.0; K],
3231            h: [0.0; H],
3232            gradient_mask: 0,
3233            hessian_mask: 0,
3234            pattern: std::marker::PhantomData,
3235        }
3236    }
3237
3238    #[inline]
3239    fn variable(x: f64, axis: usize) -> Self {
3240        let mut out = Self::constant(x);
3241        if axis < K {
3242            out.g[axis] = 1.0;
3243            out.gradient_mask = 1u128 << axis;
3244        }
3245        out
3246    }
3247}
3248
3249impl<P, const K: usize, const H: usize> crate::nested_dual::JetField for PatternedOrder2<P, K, H>
3250where
3251    P: HessianPattern<K, H>,
3252{
3253    #[inline]
3254    fn value(&self) -> f64 {
3255        self.v
3256    }
3257
3258    #[inline]
3259    fn add(&self, other: &Self) -> Self {
3260        let mut out = Self::constant(self.v + other.v);
3261        out.gradient_mask = self.gradient_mask | other.gradient_mask;
3262        let mut gradient_mask = out.gradient_mask;
3263        while gradient_mask != 0 {
3264            let i = gradient_mask.trailing_zeros() as usize;
3265            gradient_mask &= gradient_mask - 1;
3266            out.g[i] = self.g[i] + other.g[i];
3267        }
3268        out.hessian_mask = self.hessian_mask | other.hessian_mask;
3269        let mut hessian_mask = out.hessian_mask;
3270        while hessian_mask != 0 {
3271            let slot = hessian_mask.trailing_zeros() as usize;
3272            hessian_mask &= hessian_mask - 1;
3273            out.h[slot] = self.h[slot] + other.h[slot];
3274        }
3275        out
3276    }
3277
3278    #[inline]
3279    fn sub(&self, other: &Self) -> Self {
3280        let mut out = Self::constant(self.v - other.v);
3281        out.gradient_mask = self.gradient_mask | other.gradient_mask;
3282        let mut gradient_mask = out.gradient_mask;
3283        while gradient_mask != 0 {
3284            let i = gradient_mask.trailing_zeros() as usize;
3285            gradient_mask &= gradient_mask - 1;
3286            out.g[i] = self.g[i] - other.g[i];
3287        }
3288        out.hessian_mask = self.hessian_mask | other.hessian_mask;
3289        let mut hessian_mask = out.hessian_mask;
3290        while hessian_mask != 0 {
3291            let slot = hessian_mask.trailing_zeros() as usize;
3292            hessian_mask &= hessian_mask - 1;
3293            out.h[slot] = self.h[slot] - other.h[slot];
3294        }
3295        out
3296    }
3297
3298    #[inline]
3299    fn mul(&self, other: &Self) -> Self {
3300        let mut out = Self::constant(self.v * other.v);
3301        out.gradient_mask = self.gradient_mask | other.gradient_mask;
3302        let mut gradient_mask = out.gradient_mask;
3303        while gradient_mask != 0 {
3304            let i = gradient_mask.trailing_zeros() as usize;
3305            gradient_mask &= gradient_mask - 1;
3306            out.g[i] = self.v * other.g[i] + self.g[i] * other.v;
3307        }
3308        out.hessian_mask = self.hessian_mask
3309            | other.hessian_mask
3310            | Self::pair_mask_between(self.gradient_mask, other.gradient_mask);
3311        let mut hessian_mask = out.hessian_mask;
3312        while hessian_mask != 0 {
3313            let slot = hessian_mask.trailing_zeros() as usize;
3314            hessian_mask &= hessian_mask - 1;
3315            let (i, j) = P::PAIRS[slot];
3316            out.h[slot] = self.v * other.h[slot]
3317                + self.g[i] * other.g[j]
3318                + self.g[j] * other.g[i]
3319                + self.h[slot] * other.v;
3320        }
3321        out
3322    }
3323
3324    #[inline]
3325    fn neg(&self) -> Self {
3326        self.scale(-1.0)
3327    }
3328
3329    #[inline]
3330    fn scale(&self, scale: f64) -> Self {
3331        let mut out = Self::constant(self.v * scale);
3332        out.gradient_mask = self.gradient_mask;
3333        let mut gradient_mask = out.gradient_mask;
3334        while gradient_mask != 0 {
3335            let i = gradient_mask.trailing_zeros() as usize;
3336            gradient_mask &= gradient_mask - 1;
3337            out.g[i] = self.g[i] * scale;
3338        }
3339        out.hessian_mask = self.hessian_mask;
3340        let mut hessian_mask = out.hessian_mask;
3341        while hessian_mask != 0 {
3342            let slot = hessian_mask.trailing_zeros() as usize;
3343            hessian_mask &= hessian_mask - 1;
3344            out.h[slot] = self.h[slot] * scale;
3345        }
3346        out
3347    }
3348
3349    #[inline]
3350    fn compose_unary(&self, derivatives: [f64; 5]) -> Self {
3351        let mut out = Self::constant(derivatives[0]);
3352        out.gradient_mask = self.gradient_mask;
3353        let mut gradient_mask = out.gradient_mask;
3354        while gradient_mask != 0 {
3355            let i = gradient_mask.trailing_zeros() as usize;
3356            gradient_mask &= gradient_mask - 1;
3357            out.g[i] = derivatives[1] * self.g[i];
3358        }
3359        out.hessian_mask =
3360            self.hessian_mask | Self::pair_mask_between(self.gradient_mask, self.gradient_mask);
3361        let mut hessian_mask = out.hessian_mask;
3362        while hessian_mask != 0 {
3363            let slot = hessian_mask.trailing_zeros() as usize;
3364            hessian_mask &= hessian_mask - 1;
3365            let (i, j) = P::PAIRS[slot];
3366            out.h[slot] = derivatives[2] * self.g[i] * self.g[j] + derivatives[1] * self.h[slot];
3367        }
3368        out
3369    }
3370}
3371
3372// ── Order2<K>: value / gradient / Hessian (doc §A.1) ────────────────────
3373
3374/// Truncated SECOND-order scalar: value `v`, gradient `g_a`, Hessian `H_{ab}`.
3375///
3376/// This is a thin newtype over the production [`crate::jet_tower::Tower2`], so
3377/// its `(v, g, H)` channels are obtained by the SAME formulas — and are
3378/// therefore bit-identical to both [`crate::jet_tower::Tower2`] and the order-≤2
3379/// channels of a full [`crate::jet_tower::Tower4`] (doc §A.1, "Bit-identity with
3380/// the full tower"). The wrapper exists only to satisfy the generic
3381/// [`JetScalar`] interface (the `compose_unary` / `add` / `sub` / `neg` /
3382/// `recip` the trait demands, which `Tower2` does not expose by that shape) —
3383/// every channel is delegated to `Tower2` arithmetic unchanged.
3384#[derive(Clone, Copy, Debug)]
3385pub struct Order2<const K: usize>(pub crate::jet_tower::Tower2<K>);
3386
3387impl<const K: usize> Order2<K> {
3388    /// Read the gradient channel `g_a = ∂ℓ/∂p_a`.
3389    #[inline]
3390    #[must_use]
3391    pub fn g(&self) -> &[f64; K] {
3392        &self.0.g
3393    }
3394
3395    /// Read the Hessian channel.
3396    #[inline]
3397    #[must_use]
3398    pub fn h(&self) -> &[[f64; K]; K] {
3399        &self.0.h
3400    }
3401
3402    /// Consume the jet and move out its value, gradient, and Hessian channels.
3403    #[inline]
3404    #[must_use]
3405    pub fn into_channels(self) -> (f64, [f64; K], [[f64; K]; K]) {
3406        let crate::jet_tower::Tower2 { v, g, h } = self.0;
3407        (v, g, h)
3408    }
3409}
3410
3411impl<const K: usize> JetScalar<K> for Order2<K> {
3412    fn constant(c: f64) -> Self {
3413        Order2(crate::jet_tower::Tower2::constant(c))
3414    }
3415    fn variable(x: f64, axis: usize) -> Self {
3416        Order2(crate::jet_tower::Tower2::variable(x, axis))
3417    }
3418
3419    #[inline(always)]
3420    fn symmetric_quadratic_form<C: SymmetricQuadraticCoefficients>(
3421        inputs: &[Self],
3422        coefficients: &C,
3423    ) -> Self {
3424        assert_eq!(inputs.len(), coefficients.dimension());
3425        let input_dimension = inputs.len();
3426        assert!(input_dimension <= K);
3427        let mut values = [0.0; K];
3428        for axis in 0..input_dimension {
3429            values[axis] = inputs[axis].0.v;
3430        }
3431        let mut projected = [0.0; K];
3432        coefficients.multiply(
3433            &values[..input_dimension],
3434            &mut projected[..input_dimension],
3435        );
3436
3437        let mut out = crate::jet_tower::Tower2::zero();
3438        for axis in 0..input_dimension {
3439            out.v += values[axis] * projected[axis];
3440        }
3441        for primary in 0..K {
3442            let mut channel = 0.0;
3443            for axis in 0..input_dimension {
3444                channel += projected[axis] * inputs[axis].0.g[primary];
3445            }
3446            out.g[primary] = 2.0 * channel;
3447        }
3448        let mut input_gradient = [0.0; K];
3449        let mut projected_gradient = [0.0; K];
3450        for primary_b in 0..K {
3451            for row in 0..input_dimension {
3452                input_gradient[row] = inputs[row].0.g[primary_b];
3453            }
3454            coefficients.multiply(
3455                &input_gradient[..input_dimension],
3456                &mut projected_gradient[..input_dimension],
3457            );
3458            for primary_a in 0..=primary_b {
3459                let mut inherited = 0.0;
3460                let mut curvature = 0.0;
3461                for row in 0..input_dimension {
3462                    inherited += projected[row] * inputs[row].0.h[primary_a][primary_b];
3463                    curvature += inputs[row].0.g[primary_a] * projected_gradient[row];
3464                }
3465                let channel = 2.0 * (inherited + curvature);
3466                out.h[primary_a][primary_b] = channel;
3467                out.h[primary_b][primary_a] = channel;
3468            }
3469        }
3470        Order2(out)
3471    }
3472
3473    #[inline(always)]
3474    fn linear_combination(inputs: &[Self], weights: &[f64]) -> Self {
3475        assert_eq!(inputs.len(), weights.len());
3476        let mut out = crate::jet_tower::Tower2::zero();
3477        for (input, &weight) in inputs.iter().zip(weights) {
3478            out.v += input.0.v * weight;
3479        }
3480        for primary in 0..K {
3481            for (input, &weight) in inputs.iter().zip(weights) {
3482                out.g[primary] += input.0.g[primary] * weight;
3483            }
3484            for other in primary..K {
3485                for (input, &weight) in inputs.iter().zip(weights) {
3486                    out.h[primary][other] += input.0.h[primary][other] * weight;
3487                }
3488                out.h[other][primary] = out.h[primary][other];
3489            }
3490        }
3491        Order2(out)
3492    }
3493
3494    #[inline(always)]
3495    fn add_constant(&self, constant: f64) -> Self {
3496        let mut out = *self;
3497        out.0.v += constant;
3498        out
3499    }
3500
3501    #[inline(always)]
3502    fn multiply_add(&self, right: &Self, addend: &Self) -> Self {
3503        let mut out = crate::jet_tower::Tower2::zero();
3504        out.v = self.0.v * right.0.v + addend.0.v;
3505        for primary in 0..K {
3506            out.g[primary] =
3507                self.0.v * right.0.g[primary] + self.0.g[primary] * right.0.v + addend.0.g[primary];
3508            for other in primary..K {
3509                let channel = self.0.v * right.0.h[primary][other]
3510                    + self.0.g[primary] * right.0.g[other]
3511                    + self.0.g[other] * right.0.g[primary]
3512                    + self.0.h[primary][other] * right.0.v
3513                    + addend.0.h[primary][other];
3514                out.h[primary][other] = channel;
3515                out.h[other][primary] = channel;
3516            }
3517        }
3518        Order2(out)
3519    }
3520
3521    #[inline(always)]
3522    fn product(&self, right: &Self) -> Self {
3523        let mut out = crate::jet_tower::Tower2::zero();
3524        out.v = self.0.v * right.0.v;
3525        for primary in 0..K {
3526            out.g[primary] = self.0.v * right.0.g[primary] + self.0.g[primary] * right.0.v;
3527            for other in primary..K {
3528                let channel = self.0.v * right.0.h[primary][other]
3529                    + self.0.g[primary] * right.0.g[other]
3530                    + self.0.g[other] * right.0.g[primary]
3531                    + self.0.h[primary][other] * right.0.v;
3532                out.h[primary][other] = channel;
3533                out.h[other][primary] = channel;
3534            }
3535        }
3536        Order2(out)
3537    }
3538
3539    #[inline(always)]
3540    fn affine_compose(
3541        &self,
3542        input_scale: f64,
3543        input_shift: f64,
3544        derivative_stack: [f64; 5],
3545    ) -> Self {
3546        assert!(input_shift.is_finite(), "affine input shift must be finite");
3547        let first = derivative_stack[1] * input_scale;
3548        let second = derivative_stack[2] * input_scale * input_scale;
3549        let mut out = crate::jet_tower::Tower2::zero();
3550        out.v = derivative_stack[0];
3551        for primary in 0..K {
3552            out.g[primary] = first * self.0.g[primary];
3553            for other in primary..K {
3554                let channel =
3555                    first * self.0.h[primary][other] + second * self.0.g[primary] * self.0.g[other];
3556                out.h[primary][other] = channel;
3557                out.h[other][primary] = channel;
3558            }
3559        }
3560        Order2(out)
3561    }
3562
3563    #[inline(always)]
3564    fn affine_composed_sum(
3565        inputs: &[Self],
3566        input_scales: &[f64],
3567        derivative_stacks: &[[f64; 5]],
3568    ) -> Self {
3569        assert_eq!(inputs.len(), input_scales.len());
3570        assert_eq!(inputs.len(), derivative_stacks.len());
3571        let mut out = crate::jet_tower::Tower2::zero();
3572        for ((input, &input_scale), stack) in inputs.iter().zip(input_scales).zip(derivative_stacks)
3573        {
3574            let first = stack[1] * input_scale;
3575            let second = stack[2] * input_scale * input_scale;
3576            out.v += stack[0];
3577            for primary in 0..K {
3578                out.g[primary] += first * input.0.g[primary];
3579                for other in primary..K {
3580                    out.h[primary][other] += first * input.0.h[primary][other]
3581                        + second * input.0.g[primary] * input.0.g[other];
3582                }
3583            }
3584        }
3585        for primary in 0..K {
3586            for other in primary + 1..K {
3587                out.h[other][primary] = out.h[primary][other];
3588            }
3589        }
3590        Order2(out)
3591    }
3592
3593    #[inline(always)]
3594    fn shared_multiply_add_affine_composed_sum<const N: usize>(
3595        lefts: &[&Self; N],
3596        right: &Self,
3597        addend: &Self,
3598        addend_scales: &[f64; N],
3599        input_scales: &[f64; N],
3600        derivative_stacks: &[[f64; 5]; N],
3601    ) -> Self {
3602        let (representatives, term_sources, source_count) =
3603            canonical_shared_source_schedule::<N>(|term, representative| {
3604                std::ptr::eq(lefts[term], lefts[representative])
3605                    && addend_scales[term] == addend_scales[representative]
3606            });
3607        let (value, source_derivatives) =
3608            aggregate_shared_source_derivatives(&term_sources, input_scales, derivative_stacks);
3609        let mut source_gradients = [[0.0; K]; N];
3610        let mut out = crate::jet_tower::Tower2::zero();
3611        out.v = value;
3612        let mut right_first = 0.0;
3613        let mut addend_first = 0.0;
3614        for source in 0..source_count {
3615            let term = representatives[source];
3616            let first = source_derivatives[source][1];
3617            right_first += first * lefts[term].0.v;
3618            addend_first += first * addend_scales[term];
3619            for primary in 0..K {
3620                let product_gradient =
3621                    lefts[term].0.v * right.0.g[primary] + lefts[term].0.g[primary] * right.0.v;
3622                let inner_gradient = if addend_scales[term] == 0.0 {
3623                    product_gradient
3624                } else if addend_scales[term] == 1.0 {
3625                    product_gradient + addend.0.g[primary]
3626                } else {
3627                    product_gradient + addend_scales[term] * addend.0.g[primary]
3628                };
3629                source_gradients[source][primary] = inner_gradient;
3630                out.g[primary] += first * lefts[term].0.g[primary] * right.0.v;
3631            }
3632        }
3633        if N != 0 {
3634            for primary in 0..K {
3635                out.g[primary] += right_first * right.0.g[primary];
3636            }
3637        }
3638        let addend_live = addend_scales.iter().any(|&scale| scale != 0.0);
3639        if addend_live {
3640            for primary in 0..K {
3641                out.g[primary] += addend_first * addend.0.g[primary];
3642            }
3643        }
3644        for primary in 0..K {
3645            for other in primary..K {
3646                let mut channel = if N == 0 {
3647                    0.0
3648                } else {
3649                    right_first * right.0.h[primary][other]
3650                };
3651                if addend_live {
3652                    channel += addend_first * addend.0.h[primary][other];
3653                }
3654                for source in 0..source_count {
3655                    let term = representatives[source];
3656                    let local_product_hessian = lefts[term].0.g[primary] * right.0.g[other]
3657                        + lefts[term].0.g[other] * right.0.g[primary]
3658                        + lefts[term].0.h[primary][other] * right.0.v;
3659                    channel += source_derivatives[source][1] * local_product_hessian
3660                        + source_derivatives[source][2]
3661                            * source_gradients[source][primary]
3662                            * source_gradients[source][other];
3663                }
3664                out.h[primary][other] = channel;
3665                out.h[other][primary] = channel;
3666            }
3667        }
3668        Order2(out)
3669    }
3670
3671    #[inline(always)]
3672    fn composed_sum(inputs: &[Self], derivative_stacks: &[[f64; 5]]) -> Self {
3673        assert_eq!(inputs.len(), derivative_stacks.len());
3674        let mut out = crate::jet_tower::Tower2::zero();
3675        for (input, stack) in inputs.iter().zip(derivative_stacks) {
3676            out.v += stack[0];
3677            for primary in 0..K {
3678                out.g[primary] += stack[1] * input.0.g[primary];
3679                for other in primary..K {
3680                    out.h[primary][other] += stack[1] * input.0.h[primary][other]
3681                        + stack[2] * input.0.g[primary] * input.0.g[other];
3682                }
3683            }
3684        }
3685        for primary in 0..K {
3686            for other in primary + 1..K {
3687                out.h[other][primary] = out.h[primary][other];
3688            }
3689        }
3690        Order2(out)
3691    }
3692}
3693
3694impl<const K: usize> crate::nested_dual::JetField for Order2<K> {
3695    fn value(&self) -> f64 {
3696        self.0.v
3697    }
3698    fn add(&self, o: &Self) -> Self {
3699        Order2(self.0 + o.0)
3700    }
3701    fn sub(&self, o: &Self) -> Self {
3702        // Tower2 has no Sub op; subtract by adding the negation, matching
3703        // Tower4::sub (self + o.scale(-1.0)).
3704        Order2(self.0 + o.0.scale(-1.0))
3705    }
3706    fn mul(&self, o: &Self) -> Self {
3707        Order2(crate::jet_tower::Tower2::mul(&self.0, &o.0))
3708    }
3709    fn neg(&self) -> Self {
3710        Order2(self.0.scale(-1.0))
3711    }
3712    fn scale(&self, s: f64) -> Self {
3713        Order2(self.0.scale(s))
3714    }
3715    fn compose_unary(&self, d: [f64; 5]) -> Self {
3716        // Order-≤2 reads only [f, f', f''] of the stack.
3717        Order2(self.0.compose_unary([d[0], d[1], d[2]]))
3718    }
3719    fn constant_like(&self, v: f64) -> Self {
3720        // `Tower2::compose_unary([v, 0, 0])` writes `v` and then multiplies
3721        // every gradient/Hessian channel by an exact zero; skip the K + K²
3722        // multiply-adds and construct the constant. (#932)
3723        <Self as JetScalar<K>>::constant(v)
3724    }
3725    fn with_value(&self, v: f64) -> Self {
3726        let mut out = *self;
3727        out.0.v = v;
3728        out
3729    }
3730}
3731
3732/// Static lowering target for a sum of composed, low-dimensional row atoms.
3733///
3734/// A row likelihood often depends on a large global primary vector only through
3735/// a few small independent indices.  Evaluating the whole expression in an
3736/// `Order2<K>` then pays dense `K²` arithmetic at every intermediate; carrying
3737/// runtime dependency masks replaces that arithmetic with branches and bit
3738/// scans.  This accumulator provides the ahead-of-time alternative: evaluate
3739/// each index once in its natural local dimension `N`, then scatter the exact
3740/// second-order composition into the global `K` channels through a fixed axis
3741/// map.  The family still owns only its scalar index expression and certified
3742/// unary derivative stack; this type owns the universal chain rule.
3743///
3744/// For an atom `q(x_local)` and outer stack `[f(q), f'(q), f''(q)]`, the lowered
3745/// channels are
3746///
3747/// ```text
3748/// g[a_i]       += f' q_i
3749/// H[a_i, a_j] += f' q_ij + f'' q_i q_j.
3750/// ```
3751///
3752/// Only the local upper triangle is evaluated, then mirrored into the global
3753/// symmetric output.  With literal `axes` and fixed `N`, LLVM unrolls this into
3754/// straight-line arithmetic: no dependency masks, sparse-pair lookups, or jet
3755/// temporaries survive into the generated schedule.
3756#[derive(Clone, Copy, Debug)]
3757pub struct MappedOrder2Accumulator<const K: usize> {
3758    value: f64,
3759    gradient: [f64; K],
3760    hessian: [[f64; K]; K],
3761}
3762
3763/// Compile-time-symbolic value/gradient/Hessian of one local row atom.
3764///
3765/// The Hessian stores only its upper triangle, in row-major triangular order.
3766/// Instances are emitted by `gam-row-macros`; unlike a runtime forward jet,
3767/// they contain only final live channels and therefore introduce no seeded
3768/// identity arrays, dependency masks, or zero arithmetic.
3769#[derive(Clone, Copy, Debug)]
3770pub struct StaticOrder2Atom<
3771    const N: usize,
3772    const H: usize,
3773    const GRADIENT_BITS: u128,
3774    const HESSIAN_BITS: u128,
3775> {
3776    value: f64,
3777    gradient: [f64; N],
3778    hessian: [f64; H],
3779}
3780
3781impl<const N: usize, const H: usize, const G: u128, const Q: u128> StaticOrder2Atom<N, H, G, Q> {
3782    /// Construct a generated atom. `H` must equal `N(N+1)/2`.
3783    #[inline(always)]
3784    #[must_use]
3785    pub fn new(value: f64, gradient: [f64; N], hessian: [f64; H]) -> Self {
3786        assert!(H == N * (N + 1) / 2, "invalid packed order-two shape");
3787        assert!(N <= 128 && H <= 128, "static atom sparsity mask overflow");
3788        Self {
3789            value,
3790            gradient,
3791            hessian,
3792        }
3793    }
3794
3795    /// Scalar value of the generated atom.
3796    #[inline(always)]
3797    #[must_use]
3798    pub fn value(&self) -> f64 {
3799        self.value
3800    }
3801
3802    /// Generated local gradient.
3803    #[inline(always)]
3804    #[must_use]
3805    pub fn gradient(&self) -> [f64; N] {
3806        self.gradient
3807    }
3808
3809    /// Generated local Hessian entry. The matrix is symmetric.
3810    #[inline(always)]
3811    #[must_use]
3812    pub fn hessian_at(&self, row: usize, column: usize) -> f64 {
3813        assert!(
3814            row < N && column < N,
3815            "static atom Hessian axis out of range"
3816        );
3817        let (row, column) = if row <= column {
3818            (row, column)
3819        } else {
3820            (column, row)
3821        };
3822        let index = row * (2 * N - row + 1) / 2 + column - row;
3823        self.hessian[index]
3824    }
3825}
3826
3827/// Order-two channel reader accepted by [`MappedOrder2Accumulator`].
3828///
3829/// Both ordinary forward jets and build-time-symbolic atoms implement this
3830/// interface. The accumulator owns the only global scatter/chain rule.
3831pub trait Order2AtomChannels<const N: usize> {
3832    /// Structurally live local gradient channels.
3833    const GRADIENT_BITS: u128;
3834    /// Structurally live packed upper-Hessian channels.
3835    const HESSIAN_BITS: u128;
3836    /// Local gradient entry.
3837    fn gradient_at(&self, axis: usize) -> f64;
3838    /// Local Hessian entry.
3839    fn hessian_at(&self, row: usize, column: usize) -> f64;
3840}
3841
3842impl<const N: usize> Order2AtomChannels<N> for Order2<N> {
3843    const GRADIENT_BITS: u128 = low_mask(N);
3844    const HESSIAN_BITS: u128 = low_mask(N * (N + 1) / 2);
3845
3846    #[inline(always)]
3847    fn gradient_at(&self, axis: usize) -> f64 {
3848        self.0.g[axis]
3849    }
3850
3851    #[inline(always)]
3852    fn hessian_at(&self, row: usize, column: usize) -> f64 {
3853        self.0.h[row][column]
3854    }
3855}
3856
3857impl<const N: usize, const H: usize, const G: u128, const Q: u128> Order2AtomChannels<N>
3858    for StaticOrder2Atom<N, H, G, Q>
3859{
3860    const GRADIENT_BITS: u128 = G;
3861    const HESSIAN_BITS: u128 = Q;
3862
3863    #[inline(always)]
3864    fn gradient_at(&self, axis: usize) -> f64 {
3865        self.gradient[axis]
3866    }
3867
3868    #[inline(always)]
3869    fn hessian_at(&self, row: usize, column: usize) -> f64 {
3870        StaticOrder2Atom::hessian_at(self, row, column)
3871    }
3872}
3873
3874const fn low_mask(channels: usize) -> u128 {
3875    if channels >= 128 {
3876        u128::MAX
3877    } else {
3878        (1u128 << channels) - 1
3879    }
3880}
3881
3882impl<const K: usize> MappedOrder2Accumulator<K> {
3883    /// Empty additive accumulator.
3884    #[inline(always)]
3885    #[must_use]
3886    pub fn zero() -> Self {
3887        Self {
3888            value: 0.0,
3889            gradient: [0.0; K],
3890            hessian: [[0.0; K]; K],
3891        }
3892    }
3893
3894    /// Scatter `f(atom)` using the exact order-two Faà di Bruno rule.
3895    ///
3896    /// `axes[i]` maps local derivative axis `i` to its global primary axis. The
3897    /// map must be injective. Repeated axes describe a non-injective linear
3898    /// pullback whose identified cross terms need multiplicity that this simple
3899    /// scatter deliberately does not represent.
3900    #[inline(always)]
3901    pub fn add_composed<const N: usize, const H: usize, A: Order2AtomChannels<N>>(
3902        &mut self,
3903        atom: &A,
3904        axes: [usize; N],
3905        derivatives: [f64; 3],
3906        value_add: bool,
3907        gradient_add: [bool; N],
3908        hessian_add: [bool; H],
3909    ) {
3910        assert!(H == N * (N + 1) / 2, "invalid mapped Hessian write shape");
3911        assert!(N <= 128 && H <= 128, "mapped atom sparsity mask overflow");
3912        assert!(
3913            axes.iter().all(|&axis| axis < K),
3914            "mapped atom axis must be within the global primary dimension"
3915        );
3916        assert!(
3917            axes.iter()
3918                .enumerate()
3919                .all(|(i, axis)| !axes[..i].contains(axis)),
3920            "mapped atom axes must be injective"
3921        );
3922
3923        if value_add {
3924            self.value += derivatives[0];
3925        } else {
3926            self.value = derivatives[0];
3927        }
3928        let mut packed = 0;
3929        for local_i in 0..N {
3930            let global_i = axes[local_i];
3931            if A::GRADIENT_BITS & (1u128 << local_i) != 0 {
3932                let channel = derivatives[1] * atom.gradient_at(local_i);
3933                if gradient_add[local_i] {
3934                    self.gradient[global_i] += channel;
3935                } else {
3936                    self.gradient[global_i] = channel;
3937                }
3938            }
3939            for local_j in local_i..N {
3940                let global_j = axes[local_j];
3941                let inner_live = A::HESSIAN_BITS & (1u128 << packed) != 0;
3942                let outer_live = A::GRADIENT_BITS & (1u128 << local_i) != 0
3943                    && A::GRADIENT_BITS & (1u128 << local_j) != 0;
3944                let channel = if inner_live {
3945                    let inner = derivatives[1] * atom.hessian_at(local_i, local_j);
3946                    if outer_live {
3947                        inner
3948                            + derivatives[2] * atom.gradient_at(local_i) * atom.gradient_at(local_j)
3949                    } else {
3950                        inner
3951                    }
3952                } else if outer_live {
3953                    derivatives[2] * atom.gradient_at(local_i) * atom.gradient_at(local_j)
3954                } else {
3955                    packed += 1;
3956                    continue;
3957                };
3958                if hessian_add[packed] {
3959                    self.hessian[global_i][global_j] += channel;
3960                    if global_i != global_j {
3961                        self.hessian[global_j][global_i] += channel;
3962                    }
3963                } else {
3964                    self.hessian[global_i][global_j] = channel;
3965                    if global_i != global_j {
3966                        self.hessian[global_j][global_i] = channel;
3967                    }
3968                }
3969                packed += 1;
3970            }
3971        }
3972    }
3973
3974    /// Finish the lowering in the standard row-kernel channel layout.
3975    #[inline(always)]
3976    #[must_use]
3977    pub fn into_channels(self) -> (f64, [f64; K], [[f64; K]; K]) {
3978        (self.value, self.gradient, self.hessian)
3979    }
3980}
3981
3982/// One inner scalar in a runtime-width additive order-two composition.
3983///
3984/// Implementors expose only the inner gradient and Hessian. The corresponding
3985/// outer function's first and second derivatives live beside the source in the
3986/// caller's term type, so several terms that share an inner scalar may be fused
3987/// before entering [`DynamicOrder2Accumulator`].
3988pub trait DynamicOrder2Term {
3989    /// Outer first derivative `f'(q)` for this already-fused source.
3990    fn outer_first(&self) -> f64;
3991
3992    /// Outer second derivative `f''(q)` for this already-fused source.
3993    fn outer_second(&self) -> f64;
3994
3995    /// Inner first derivative `q_i`.
3996    fn inner_gradient(&self, axis: usize) -> f64;
3997
3998    /// Inner second derivative `q_ij`.
3999    fn inner_hessian(&self, row: usize, column: usize) -> f64;
4000}
4001
4002/// Allocation-minimal runtime-width lowering of an additive order-two program.
4003///
4004/// This is the dynamic-width sibling of [`MappedOrder2Accumulator`]. A caller
4005/// first reduces its mathematical row program to a scalar value plus `N`
4006/// independent composed sources. This accumulator then performs the universal
4007/// chain rule
4008///
4009/// ```text
4010/// g_i  = sum_t (f_t' q_t,i)
4011/// H_ij = sum_t (f_t'' q_t,i q_t,j + f_t' q_t,ij)
4012/// ```
4013///
4014/// in one gradient pass and one upper-triangular Hessian pass. It owns exactly
4015/// the two buffers that become the returned gradient and Hessian; no per-term
4016/// derivative buffer or jet temporary is allocated. `N` is const-generic so a
4017/// small fixed source set is visible to LLVM and can be unrolled.
4018#[derive(Debug)]
4019pub struct DynamicOrder2Accumulator {
4020    value: f64,
4021    gradient: Vec<f64>,
4022    hessian: Vec<f64>,
4023}
4024
4025impl DynamicOrder2Accumulator {
4026    /// Lower a fused additive source set at runtime primary width `dimension`.
4027    #[inline(always)]
4028    #[must_use]
4029    pub fn from_composed_sum<T: DynamicOrder2Term, const N: usize>(
4030        dimension: usize,
4031        value: f64,
4032        terms: &[T; N],
4033    ) -> Self {
4034        let mut gradient = vec![0.0; dimension];
4035        let mut hessian = vec![0.0; dimension * dimension];
4036
4037        for axis in 0..dimension {
4038            let mut channel = 0.0;
4039            for term in terms {
4040                channel += term.outer_first() * term.inner_gradient(axis);
4041            }
4042            gradient[axis] = channel;
4043        }
4044
4045        for row in 0..dimension {
4046            for column in row..dimension {
4047                let mut channel = 0.0;
4048                for term in terms {
4049                    let row_gradient = term.inner_gradient(row);
4050                    let column_gradient = term.inner_gradient(column);
4051                    channel += term.outer_second() * row_gradient * column_gradient
4052                        + term.outer_first() * term.inner_hessian(row, column);
4053                }
4054                hessian[row * dimension + column] = channel;
4055                hessian[column * dimension + row] = channel;
4056            }
4057        }
4058
4059        Self {
4060            value,
4061            gradient,
4062            hessian,
4063        }
4064    }
4065
4066    /// Finish the lowering as `(value, gradient, row-major symmetric Hessian)`.
4067    #[inline(always)]
4068    #[must_use]
4069    pub fn into_channels(self) -> (f64, Vec<f64>, Vec<f64>) {
4070        (self.value, self.gradient, self.hessian)
4071    }
4072}
4073
4074// ── Lane-batched Order-2 scalar: 4 rows per pass in SIMD lanes (perf) ────
4075//
4076// The hot per-row jet kernels evaluate ONE row's `(v, g, H)` tower at a time in
4077// scalar `f64`. A hand-written scalar derivative does the same. The throughput
4078// lever a jet has that scalar hand-code cannot is **row batching in SIMD
4079// lanes**: the order-≤2 Leibniz product `Order2::mul` is `O(K²)` independent
4080// per-channel float ops, and EVERY row runs the identical op graph on different
4081// data — the textbook SPMD shape. Packing `LANES = 4` rows into a `wide::f64x4`
4082// and running the algebra once per 4 rows replaces 4 scalar passes with one
4083// vector pass: the `K²` Hessian channel updates become `K²` NEON `.2d` / SSE2
4084// `pd` instructions covering 4 rows each, ~4× fewer FP instructions per row.
4085//
4086// The carried scalar field is abstracted by [`Lane`] so the SAME algebra body
4087// instantiates at `f64` (1 row, used as the bit-identity oracle) or
4088// [`wide::f64x4`] (4 rows). Bit-identity is structural, not approximate:
4089//
4090//   * Every arithmetic op is a plain lane-wise `+` / `-` / `*` (NEVER a fused
4091//     `mul_add`), and IEEE-754 double `+`/`-`/`*`/`/` are correctly rounded and
4092//     deterministic, so lane `i` of an `f64x4` op equals the scalar `f64` op on
4093//     that lane's inputs bit-for-bit.
4094//   * The transcendental derivative STACKS (`exp`/`ln`/`sqrt`/…) are produced
4095//     **per lane by the identical scalar code** ([`Lane::unary3`] unpacks, runs
4096//     the same `[f64; 3]` stack closure the scalar path runs, repacks), so the
4097//     only thing vectorised is the cheap rational tensor composition — the
4098//     library transcendental itself is the exact same `f64::exp` call per lane.
4099//   * The op order mirrors [`crate::jet_tower::Tower2`] term-for-term, so
4100//     [`Order2Lane<f64, K>`] is `to_bits`-identical to the production
4101//     [`Order2<K>`] (= `Tower2<K>`), and [`Order2Lane<f64x4, K>`] lane `i` is
4102//     `to_bits`-identical to that — proven by the `batch_tests` oracle below
4103//     (≥2000 random 4-row batches across `K ∈ {2,3,4,9}`).
4104
4105/// The scalar field a [`Order2Lane`] carries: either a single `f64` (one row,
4106/// the oracle) or a [`wide::f64x4`] (four rows evaluated in SIMD lanes). All ops
4107/// are plain lane-wise IEEE arithmetic, so a vector op equals the scalar op on
4108/// each lane bit-for-bit.
4109pub trait Lane: Copy {
4110    /// How many rows one value of this field carries: 1 for the `f64` oracle,
4111    /// 4 for [`wide::f64x4`]. [`Lane::lane`] indices are bounded by this.
4112    const LANES: usize;
4113    /// Broadcast a scalar to every lane.
4114    fn splat(x: f64) -> Self;
4115    /// Lane-wise `self + o`.
4116    fn add(self, o: Self) -> Self;
4117    /// Lane-wise `self - o`.
4118    fn sub(self, o: Self) -> Self;
4119    /// Lane-wise `self * o`.
4120    fn mul(self, o: Self) -> Self;
4121    /// The `f64` in lane `i`. Panics when `i >= LANES` — a row index past the
4122    /// end of the field is a caller bug, not a value to silently substitute.
4123    fn lane(self, i: usize) -> f64;
4124    /// Build the order-≤2 derivative stack `[f(u), f′(u), f″(u)]` **per lane**
4125    /// from the lane value `u`, via the SAME scalar `stack` closure the
4126    /// per-row path runs (so the transcendental/rational stack is bit-identical
4127    /// to the scalar evaluation — only the subsequent tensor composition is
4128    /// vectorised).
4129    fn unary3(self, stack: impl Fn(f64) -> [f64; 3]) -> [Self; 3];
4130    /// Build the order-≤4 derivative stack `[f, f′, f″, f‴, f⁗]` **per lane**
4131    /// from the lane value `u`, via the SAME scalar `stack` closure the per-row
4132    /// path runs. The one-/two-seed scalars ([`OneSeedLane`] / [`TwoSeedLane`])
4133    /// need outer derivatives one / two orders beyond their order-2 base, so
4134    /// they build their composition stack through this five-entry variant. As
4135    /// with [`unary3`](Lane::unary3), only the transcendental/rational stack is
4136    /// evaluated per lane (bit-identically to the scalar path); the subsequent
4137    /// tensor composition is vectorised.
4138    fn unary5(self, stack: impl Fn(f64) -> [f64; 5]) -> [Self; 5];
4139}
4140
4141impl Lane for f64 {
4142    const LANES: usize = 1;
4143    #[inline]
4144    fn splat(x: f64) -> Self {
4145        x
4146    }
4147    #[inline]
4148    fn add(self, o: Self) -> Self {
4149        self + o
4150    }
4151    #[inline]
4152    fn sub(self, o: Self) -> Self {
4153        self - o
4154    }
4155    #[inline]
4156    fn mul(self, o: Self) -> Self {
4157        self * o
4158    }
4159    #[inline]
4160    fn lane(self, i: usize) -> f64 {
4161        assert!(
4162            i < <Self as Lane>::LANES,
4163            "the f64 Lane carries one row; lane {i} does not exist"
4164        );
4165        self
4166    }
4167    #[inline]
4168    fn unary3(self, stack: impl Fn(f64) -> [f64; 3]) -> [Self; 3] {
4169        stack(self)
4170    }
4171    #[inline]
4172    fn unary5(self, stack: impl Fn(f64) -> [f64; 5]) -> [Self; 5] {
4173        stack(self)
4174    }
4175}
4176
4177impl Lane for wide::f64x4 {
4178    const LANES: usize = 4;
4179    #[inline]
4180    fn splat(x: f64) -> Self {
4181        wide::f64x4::splat(x)
4182    }
4183    #[inline]
4184    fn add(self, o: Self) -> Self {
4185        self + o
4186    }
4187    #[inline]
4188    fn sub(self, o: Self) -> Self {
4189        self - o
4190    }
4191    #[inline]
4192    fn mul(self, o: Self) -> Self {
4193        self * o
4194    }
4195    #[inline]
4196    fn lane(self, i: usize) -> f64 {
4197        self.to_array()[i]
4198    }
4199    #[inline]
4200    fn unary3(self, stack: impl Fn(f64) -> [f64; 3]) -> [Self; 3] {
4201        let a = self.to_array();
4202        let mut d0 = [0.0_f64; 4];
4203        let mut d1 = [0.0_f64; 4];
4204        let mut d2 = [0.0_f64; 4];
4205        for i in 0..4 {
4206            let s = stack(a[i]);
4207            d0[i] = s[0];
4208            d1[i] = s[1];
4209            d2[i] = s[2];
4210        }
4211        [
4212            wide::f64x4::new(d0),
4213            wide::f64x4::new(d1),
4214            wide::f64x4::new(d2),
4215        ]
4216    }
4217    #[inline]
4218    fn unary5(self, stack: impl Fn(f64) -> [f64; 5]) -> [Self; 5] {
4219        let a = self.to_array();
4220        let mut d = [[0.0_f64; 4]; 5];
4221        for i in 0..4 {
4222            let s = stack(a[i]);
4223            for (k, dk) in d.iter_mut().enumerate() {
4224                dk[i] = s[k];
4225            }
4226        }
4227        [
4228            wide::f64x4::new(d[0]),
4229            wide::f64x4::new(d[1]),
4230            wide::f64x4::new(d[2]),
4231            wide::f64x4::new(d[3]),
4232            wide::f64x4::new(d[4]),
4233        ]
4234    }
4235}
4236
4237/// A lane-batched order-≤2 Taylor scalar: value / gradient / Hessian carried in
4238/// a SIMD field [`L: Lane`](Lane). With `L = f64x4` one instance carries FOUR
4239/// rows at once, so the row loop processes 4 rows per vector pass instead of one
4240/// per scalar pass.
4241///
4242/// The channel layout and every float op mirror [`crate::jet_tower::Tower2`]
4243/// term-for-term, so `Order2Lane<f64, K>` is `to_bits`-identical to the
4244/// production [`Order2<K>`] and `Order2Lane<f64x4, K>` lane `i` is
4245/// `to_bits`-identical to that (see the module note and `batch_tests`).
4246#[derive(Clone, Copy, Debug)]
4247pub struct Order2Lane<L: Lane, const K: usize> {
4248    /// Value channel `ℓ` (one entry per lane/row).
4249    pub v: L,
4250    /// Gradient channel `∂ℓ/∂p_a`.
4251    pub g: [L; K],
4252    /// Hessian channel `∂²ℓ/∂p_a∂p_b` (symmetric).
4253    pub h: [[L; K]; K],
4254}
4255
4256/// The 4-rows-per-pass batched order-≤2 scalar (`wide::f64x4` lanes).
4257pub type Order2Batch<const K: usize> = Order2Lane<wide::f64x4, K>;
4258
4259impl<L: Lane, const K: usize> Order2Lane<L, K> {
4260    /// A constant: value `c` in every channel-zero slot.
4261    #[inline]
4262    pub fn constant(c: L) -> Self {
4263        Order2Lane {
4264            v: c,
4265            g: [L::splat(0.0); K],
4266            h: [[L::splat(0.0); K]; K],
4267        }
4268    }
4269
4270    /// The seeded variable `p_axis` at (per-lane) value `value`: unit first
4271    /// derivative in slot `axis`. With `L = f64x4`, `value` packs the four
4272    /// rows' values of primary `axis`.
4273    #[inline]
4274    pub fn variable(value: L, axis: usize) -> Self {
4275        let mut out = Self::constant(value);
4276        out.g[axis] = L::splat(1.0);
4277        out
4278    }
4279
4280    /// Lane-wise `self + o` (mirrors `Tower2` Add: per-channel add).
4281    #[inline]
4282    pub fn add(&self, o: &Self) -> Self {
4283        let mut out = *self;
4284        out.v = self.v.add(o.v);
4285        for i in 0..K {
4286            out.g[i] = self.g[i].add(o.g[i]);
4287            for j in 0..K {
4288                out.h[i][j] = self.h[i][j].add(o.h[i][j]);
4289            }
4290        }
4291        out
4292    }
4293
4294    /// Multiply every channel by the plain scalar `s` (mirrors `Tower2::scale`).
4295    #[inline]
4296    pub fn scale(&self, s: f64) -> Self {
4297        let sl = L::splat(s);
4298        let mut out = *self;
4299        out.v = self.v.mul(sl);
4300        for i in 0..K {
4301            out.g[i] = self.g[i].mul(sl);
4302            for j in 0..K {
4303                out.h[i][j] = self.h[i][j].mul(sl);
4304            }
4305        }
4306        out
4307    }
4308
4309    /// Lane-wise `self - o`, expressed as `self + o·(-1)` exactly as
4310    /// [`Order2::sub`](crate::nested_dual::JetField::sub) / `Tower4::sub` do, so signed-zero handling matches.
4311    #[inline]
4312    pub fn sub(&self, o: &Self) -> Self {
4313        self.add(&o.scale(-1.0))
4314    }
4315
4316    /// Negate every channel (= `scale(-1.0)`, matching [`Order2::neg`](crate::nested_dual::JetField::neg)).
4317    #[inline]
4318    pub fn neg(&self) -> Self {
4319        self.scale(-1.0)
4320    }
4321
4322    /// Exact order-≤2 Leibniz product, term-for-term identical to
4323    /// [`crate::jet_tower::Tower2::mul`] (same factor order, no `mul_add`).
4324    ///
4325    /// The Hessian channel is symmetric under `i ↔ j` (see
4326    /// [`crate::jet_tower::Tower2::mul`] for why the invariant always holds), so
4327    /// we compute the upper triangle `j ≥ i` and mirror it — `K(K+1)/2` lane
4328    /// entry-chains instead of `K²`. Because each lane entry is already a full
4329    /// SIMD op (no cross-`j` lane packing to lose), halving the entry count is a
4330    /// direct throughput win (~18 % on `Order2Lane<f64x4, 9>`, the survival batch
4331    /// kernel, and ~2× on the `f64` oracle). The upper triangle uses the EXACT
4332    /// term order of `Tower2::mul`, so `Order2Lane<f64>` stays `to_bits`-identical
4333    /// to `Order2` (= `Tower2`) and `Order2Lane<f64x4>` lane `i` stays
4334    /// `to_bits`-identical to that; the mirror makes the batch Hessian exactly
4335    /// symmetric, matching the scalar `Tower2::mul` (which mirrors identically).
4336    #[inline]
4337    pub fn mul(&self, o: &Self) -> Self {
4338        let a = self;
4339        let b = o;
4340        let mut out = Self::constant(a.v.mul(b.v));
4341        for i in 0..K {
4342            // a.v*b.g[i] + a.g[i]*b.v
4343            out.g[i] = a.v.mul(b.g[i]).add(a.g[i].mul(b.v));
4344        }
4345        for i in 0..K {
4346            for j in i..K {
4347                // a.v*b.h + a.g[i]*b.g[j] + a.g[j]*b.g[i] + a.h*b.v
4348                let hij =
4349                    a.v.mul(b.h[i][j])
4350                        .add(a.g[i].mul(b.g[j]))
4351                        .add(a.g[j].mul(b.g[i]))
4352                        .add(a.h[i][j].mul(b.v));
4353                out.h[i][j] = hij;
4354                out.h[j][i] = hij;
4355            }
4356        }
4357        out
4358    }
4359
4360    /// Exact order-≤2 Faà di Bruno composition `f ∘ self`, given the per-lane
4361    /// derivative stack `d = [f(u), f′(u), f″(u)]`. Mirrors
4362    /// [`crate::jet_tower::Tower2::compose_unary`] term-for-term (`acc` starts at
4363    /// `0` then accumulates, so signed-zero collapses identically).
4364    #[inline]
4365    pub fn compose_unary(&self, d: [L; 3]) -> Self {
4366        let mut out = Self::constant(d[0]);
4367        for i in 0..K {
4368            let mut acc = L::splat(0.0);
4369            acc = acc.add(d[1].mul(self.g[i]));
4370            out.g[i] = acc;
4371        }
4372        for i in 0..K {
4373            for j in 0..K {
4374                let mut acc = L::splat(0.0);
4375                acc = acc.add(d[1].mul(self.h[i][j]));
4376                acc = acc.add(d[2].mul(self.g[i]).mul(self.g[j]));
4377                out.h[i][j] = acc;
4378            }
4379        }
4380        out
4381    }
4382
4383    /// `e^self`, per-lane stack `[e, e, e]` (matches the [`JetScalar::exp`]
4384    /// default forwarded through `Order2`).
4385    #[inline]
4386    pub fn exp(&self) -> Self {
4387        let d = self.v.unary3(|u| {
4388            let e = u.exp();
4389            [e, e, e]
4390        });
4391        self.compose_unary(d)
4392    }
4393
4394    /// `ln(self)`; caller guarantees positivity. Per-lane stack
4395    /// `[ln u, 1/u, -1/u²]` (matches [`JetScalar::ln`] truncated to order 2).
4396    #[inline]
4397    pub fn ln(&self) -> Self {
4398        let d = self.v.unary3(|u| {
4399            let r = 1.0 / u;
4400            [u.ln(), r, -r * r]
4401        });
4402        self.compose_unary(d)
4403    }
4404
4405    /// `√self`; caller guarantees positivity. Per-lane stack
4406    /// `[s, 0.5/s, -0.25/(u·s)]` (matches [`JetScalar::sqrt`]).
4407    #[inline]
4408    pub fn sqrt(&self) -> Self {
4409        let d = self.v.unary3(|u| {
4410            let s = u.sqrt();
4411            [s, 0.5 / s, -0.25 / (u * s)]
4412        });
4413        self.compose_unary(d)
4414    }
4415
4416    /// `1/self`. Per-lane stack `[r, -r², 2r³]` (matches [`JetScalar::recip`]).
4417    #[inline]
4418    pub fn recip(&self) -> Self {
4419        let d = self.v.unary3(|u| {
4420            let r = 1.0 / u;
4421            let r2 = r * r;
4422            [r, -r2, 2.0 * r2 * r]
4423        });
4424        self.compose_unary(d)
4425    }
4426
4427    /// `self^a` for real `a`; caller guarantees a positive base. Per-lane
4428    /// falling-factorial stack (matches [`JetScalar::powf`]).
4429    #[inline]
4430    pub fn powf(&self, a: f64) -> Self {
4431        let d = self.v.unary3(|u| {
4432            [
4433                u.powf(a),
4434                a * u.powf(a - 1.0),
4435                a * (a - 1.0) * u.powf(a - 2.0),
4436            ]
4437        });
4438        self.compose_unary(d)
4439    }
4440}
4441
4442impl<const K: usize> Order2Batch<K> {
4443    /// Extract lane `i`'s `(v, g, H)` as a production [`Order2<K>`] scalar.
4444    /// Lane `i` is `to_bits`-identical to evaluating the same program at
4445    /// [`Order2<K>`] on row `i` (see `batch_tests`).
4446    #[inline]
4447    #[must_use]
4448    pub fn lane(&self, i: usize) -> Order2<K> {
4449        let mut t = crate::jet_tower::Tower2::<K>::constant(self.v.lane(i));
4450        for a in 0..K {
4451            t.g[a] = self.g[a].lane(i);
4452            for b in 0..K {
4453                t.h[a][b] = self.h[a][b].lane(i);
4454            }
4455        }
4456        Order2(t)
4457    }
4458}
4459
4460// ── Order1<K>: value / gradient only (doc §A.1, first-order prune) ──────
4461
4462/// Truncated FIRST-order scalar: value `v` and gradient `g_a` only — NO Hessian.
4463///
4464/// This is [`Order2`] with the K×K Hessian channel deleted. Its value and
4465/// gradient are computed by the SAME order-≤1 truncation of the Leibniz / Faà
4466/// di Bruno rules that [`Order2`] uses for those two channels, with the float
4467/// operations applied in the identical order — so its `(v, g)` is BIT-IDENTICAL
4468/// to both [`Order2`]'s and a full [`crate::jet_tower::Tower4`]'s order-≤1
4469/// channels. Use it at a consumer that reads ONLY value + gradient (the SAE
4470/// β-border channel: the reconstruction is linear in β, so the Hessian-in-β
4471/// vanishes and the dense K×K Hessian product `Tower2::mul` would build is pure
4472/// discarded work). Order-≤1 value/gradient never read any input's Hessian, so
4473/// dropping that channel changes neither result nor float-op order — it only
4474/// removes the `K²` arithmetic that produced an unread tensor.
4475#[derive(Clone, Copy, Debug)]
4476pub struct Order1<const K: usize> {
4477    /// Value ℓ.
4478    pub v: f64,
4479    /// Gradient ∂ℓ/∂p_a.
4480    pub g: [f64; K],
4481}
4482
4483impl<const K: usize> Order1<K> {
4484    /// Read the gradient channel `g_a = ∂ℓ/∂p_a`.
4485    #[inline]
4486    #[must_use]
4487    pub fn g(&self) -> &[f64; K] {
4488        &self.g
4489    }
4490
4491    /// Consume the jet and move out its value and gradient channels.
4492    #[inline]
4493    #[must_use]
4494    pub fn into_channels(self) -> (f64, [f64; K]) {
4495        (self.v, self.g)
4496    }
4497}
4498
4499impl<const K: usize> JetScalar<K> for Order1<K> {
4500    fn constant(c: f64) -> Self {
4501        // Order2::constant -> Tower2::constant: value c, all derivatives zero.
4502        Order1 { v: c, g: [0.0; K] }
4503    }
4504    fn variable(x: f64, axis: usize) -> Self {
4505        // Order2::variable -> Tower2::variable: unit first derivative in `axis`.
4506        let mut g = [0.0; K];
4507        g[axis] = 1.0;
4508        Order1 { v: x, g }
4509    }
4510}
4511
4512impl<const K: usize> crate::nested_dual::JetField for Order1<K> {
4513    fn value(&self) -> f64 {
4514        self.v
4515    }
4516    fn add(&self, o: &Self) -> Self {
4517        // Tower2 Add: out.v += o.v; out.g[i] += o.g[i] (same float order).
4518        let mut g = self.g;
4519        for i in 0..K {
4520            g[i] += o.g[i];
4521        }
4522        Order1 { v: self.v + o.v, g }
4523    }
4524    fn sub(&self, o: &Self) -> Self {
4525        // Mirror Order2::sub == self + o.scale(-1.0) exactly: scale then add.
4526        self.add(&o.scale(-1.0))
4527    }
4528    fn mul(&self, o: &Self) -> Self {
4529        // Tower2::mul value/grad terms, identical float order:
4530        //   v = a.v*b.v;  g[i] = a.v*b.g[i] + a.g[i]*b.v.
4531        // (The Hessian loop `a.v*b.h + a.g*b.g + ... + a.h*b.v` is the discarded
4532        //  work this type exists to skip; it never feeds v or g.)
4533        let a = self;
4534        let b = o;
4535        let mut g = [0.0; K];
4536        for i in 0..K {
4537            g[i] = a.v * b.g[i] + a.g[i] * b.v;
4538        }
4539        Order1 { v: a.v * b.v, g }
4540    }
4541    fn neg(&self) -> Self {
4542        // Order2::neg == self.0.scale(-1.0).
4543        self.scale(-1.0)
4544    }
4545    fn scale(&self, s: f64) -> Self {
4546        // Tower2::scale: out.v *= s; out.g[i] *= s (same float order).
4547        let mut g = self.g;
4548        for i in 0..K {
4549            g[i] *= s;
4550        }
4551        Order1 { v: self.v * s, g }
4552    }
4553    fn compose_unary(&self, d: [f64; 5]) -> Self {
4554        // Faà di Bruno truncated to order ≤ 1 (matches `faa_di_bruno` /
4555        // `Tower2::compose_unary` for the value and gradient channels):
4556        //   value channel (m=0): d[0].
4557        //   grad channel (positions=[i], single partition {{0}}): d[1]·g[i].
4558        // Order-≤1 reads only d[0], d[1]; trailing stack entries are unused.
4559        let mut g = [0.0; K];
4560        for i in 0..K {
4561            g[i] = d[1] * self.g[i];
4562        }
4563        Order1 { v: d[0], g }
4564    }
4565}
4566
4567// ── OneSeed<K>: one-seed directional, contracted third (doc §A.2) ───────
4568
4569/// One-seed directional scalar: an [`Order2`] base plus ONE nilpotent ε
4570/// (`ε² = 0`) whose coefficient is itself an [`Order2`].
4571///
4572/// A scalar is `s = base + ε·eps`. Arithmetic is the `ε² = 0` truncation of the
4573/// product (doc §A.2): the base parts multiply as ordinary [`Order2`] products,
4574/// and the ε-coefficient picks up `a.base·b.eps + a.eps·b.base`. Composition
4575/// pushes ε through one extra outer derivative.
4576///
4577/// Seed each primary with [`seed_direction`](Self::seed_direction): the base is
4578/// the usual seeded variable (carrying `e_a` for the Hessian channel) and the
4579/// ε-coefficient is the FIXED contraction direction `u_a` (a constant). Then the
4580/// ε-component of the evaluated Hessian channel is the contracted third
4581/// `[eps.h][a][b] = Σ_c ℓ_{abc} u_c` — exactly `row_third_contracted(dir = u)`,
4582/// without materialising `t3`.
4583#[derive(Clone, Copy, Debug)]
4584pub struct OneSeed<const K: usize> {
4585    /// The `ε⁰` part: value / gradient / Hessian of `ℓ`.
4586    pub base: Order2<K>,
4587    /// The `ε¹` part: value / gradient / Hessian of the ε-coefficient. After a
4588    /// `seed_direction(u)` evaluation, `eps.h[a][b] = Σ_c ℓ_{abc} u_c`.
4589    pub eps: Order2<K>,
4590}
4591
4592impl<const K: usize> OneSeed<K> {
4593    /// Seed primary `axis` at value `x` with ε-direction component `u_axis`:
4594    /// `p_axis = p_axis⁰ + x-seed + ε·u_axis`, i.e. base = `variable(x, axis)`
4595    /// and eps = `constant(u_axis)` (doc §A.2 "Seeding").
4596    pub fn seed_direction(x: f64, axis: usize, u_axis: f64) -> Self {
4597        OneSeed {
4598            base: Order2::variable(x, axis),
4599            eps: Order2::constant(u_axis),
4600        }
4601    }
4602
4603    /// The contracted-third channel after a `seed_direction(u)` evaluation:
4604    /// `out[a][b] = Σ_c ℓ_{abc} u_c`, i.e. the ε-coefficient's Hessian (doc §A.2).
4605    pub fn contracted_third(&self) -> [[f64; K]; K] {
4606        *self.eps.h()
4607    }
4608}
4609
4610impl<const K: usize> JetScalar<K> for OneSeed<K> {
4611    fn constant(c: f64) -> Self {
4612        OneSeed {
4613            base: Order2::constant(c),
4614            eps: Order2::constant(0.0),
4615        }
4616    }
4617    fn variable(x: f64, axis: usize) -> Self {
4618        // No ε-direction unless seeded via `seed_direction`.
4619        OneSeed {
4620            base: Order2::variable(x, axis),
4621            eps: Order2::constant(0.0),
4622        }
4623    }
4624}
4625
4626impl<const K: usize> crate::nested_dual::JetField for OneSeed<K> {
4627    fn value(&self) -> f64 {
4628        self.base.value()
4629    }
4630    fn add(&self, o: &Self) -> Self {
4631        OneSeed {
4632            base: self.base.add(&o.base),
4633            eps: self.eps.add(&o.eps),
4634        }
4635    }
4636    fn sub(&self, o: &Self) -> Self {
4637        OneSeed {
4638            base: self.base.sub(&o.base),
4639            eps: self.eps.sub(&o.eps),
4640        }
4641    }
4642    fn mul(&self, o: &Self) -> Self {
4643        // (a.base + ε a.eps)(b.base + ε b.eps), dropping ε².  Build
4644        // the epsilon channels directly instead of materialising two complete
4645        // Order2 products and adding them.  The upper triangle is sufficient:
4646        // every input Hessian is symmetric and this polynomial preserves that
4647        // invariant exactly.
4648        let ab = &self.base.0;
4649        let ae = &self.eps.0;
4650        let bb = &o.base.0;
4651        let be = &o.eps.0;
4652        let mut eps = crate::jet_tower::Tower2::<K>::zero();
4653        eps.v = ab.v * be.v + ae.v * bb.v;
4654        for i in 0..K {
4655            eps.g[i] = ab.v * be.g[i] + ab.g[i] * be.v + ae.v * bb.g[i] + ae.g[i] * bb.v;
4656        }
4657        for i in 0..K {
4658            for j in i..K {
4659                let channel = ab.v * be.h[i][j]
4660                    + ab.g[i] * be.g[j]
4661                    + ab.g[j] * be.g[i]
4662                    + ab.h[i][j] * be.v
4663                    + ae.v * bb.h[i][j]
4664                    + ae.g[i] * bb.g[j]
4665                    + ae.g[j] * bb.g[i]
4666                    + ae.h[i][j] * bb.v;
4667                eps.h[i][j] = channel;
4668                eps.h[j][i] = channel;
4669            }
4670        }
4671        OneSeed {
4672            base: self.base.mul(&o.base),
4673            eps: Order2(eps),
4674        }
4675    }
4676    fn neg(&self) -> Self {
4677        OneSeed {
4678            base: self.base.neg(),
4679            eps: self.eps.neg(),
4680        }
4681    }
4682    fn scale(&self, s: f64) -> Self {
4683        OneSeed {
4684            base: self.base.scale(s),
4685            eps: self.eps.scale(s),
4686        }
4687    }
4688    fn compose_unary(&self, d: [f64; 5]) -> Self {
4689        // f(base + ε eps) = f(base) + ε · f'(base)·eps (ε² = 0).
4690        // Fuse the shifted composition and product into their final channels;
4691        // this removes one temporary Order2 composition and one Order2 product
4692        // from every unary primitive in a directional row program.
4693        let base = self.base.compose_unary([d[0], d[1], d[2], d[3], d[4]]);
4694        let b = &self.base.0;
4695        let e = &self.eps.0;
4696        let mut eps = crate::jet_tower::Tower2::<K>::zero();
4697        eps.v = d[1] * e.v;
4698        for i in 0..K {
4699            eps.g[i] = d[2] * b.g[i] * e.v + d[1] * e.g[i];
4700        }
4701        for i in 0..K {
4702            for j in i..K {
4703                let channel = d[1] * e.h[i][j]
4704                    + d[2] * (b.g[i] * e.g[j] + b.g[j] * e.g[i] + b.h[i][j] * e.v)
4705                    + d[3] * b.g[i] * b.g[j] * e.v;
4706                eps.h[i][j] = channel;
4707                eps.h[j][i] = channel;
4708            }
4709        }
4710        OneSeed {
4711            base,
4712            eps: Order2(eps),
4713        }
4714    }
4715    fn constant_like(&self, v: f64) -> Self {
4716        OneSeed {
4717            base: self.base.constant_like(v),
4718            eps: self.eps.constant_like(0.0),
4719        }
4720    }
4721    fn with_value(&self, v: f64) -> Self {
4722        // The real value channel is the base part's; the ε part carries no
4723        // value of its own.
4724        OneSeed {
4725            base: self.base.with_value(v),
4726            eps: self.eps,
4727        }
4728    }
4729}
4730
4731// ── OneSeedLane<L, K>: lane-batched one-seed directional (doc §A.2) ──────
4732
4733/// Lane-batched [`OneSeed`]: the same one-seed directional scalar with its two
4734/// [`Order2`] parts re-typed to [`Order2Lane<L, K>`], so one `L = f64x4`
4735/// instance carries FOUR rows' contracted-third evaluations per vector pass.
4736///
4737/// Every operation (`add`/`sub`/`mul`/`neg`/`scale`/`compose_unary` and the
4738/// transcendentals) is a term-for-term structural re-type of the scalar
4739/// [`OneSeed`] ops onto the lane-implemented [`Order2Lane`] algebra. With
4740/// `L = f64`, `OneSeedLane<f64, K>` is `to_bits`-identical to [`OneSeed<K>`];
4741/// with `L = f64x4`, lane `i` is `to_bits`-identical to that (see `batch_tests`).
4742#[derive(Clone, Copy, Debug)]
4743pub struct OneSeedLane<L: Lane, const K: usize> {
4744    /// The `ε⁰` part (lane-batched value / gradient / Hessian of `ℓ`).
4745    pub base: Order2Lane<L, K>,
4746    /// The `ε¹` part. After a `seed_direction(u)` evaluation,
4747    /// `eps.h[a][b]` lane `i` is row `i`'s `Σ_c ℓ_{abc} u_c`.
4748    pub eps: Order2Lane<L, K>,
4749}
4750
4751/// The 4-rows-per-pass batched one-seed scalar (`wide::f64x4` lanes).
4752pub type OneSeedBatch<const K: usize> = OneSeedLane<wide::f64x4, K>;
4753
4754impl<L: Lane, const K: usize> OneSeedLane<L, K> {
4755    /// A constant: base = `constant(c)`, ε-part zero (mirrors [`OneSeed::constant`]).
4756    #[inline]
4757    pub fn constant(c: L) -> Self {
4758        OneSeedLane {
4759            base: Order2Lane::constant(c),
4760            eps: Order2Lane::constant(L::splat(0.0)),
4761        }
4762    }
4763
4764    /// The seeded variable `p_axis` at (per-lane) value `value`, no ε-direction
4765    /// (mirrors [`OneSeed::variable`]).
4766    #[inline]
4767    pub fn variable(value: L, axis: usize) -> Self {
4768        OneSeedLane {
4769            base: Order2Lane::variable(value, axis),
4770            eps: Order2Lane::constant(L::splat(0.0)),
4771        }
4772    }
4773
4774    /// Seed primary `axis` at (per-lane) value `value` with ε-direction
4775    /// component `u_axis`: base = `variable(value, axis)`, eps = `constant(u_axis)`
4776    /// (mirrors [`OneSeed::seed_direction`]). With `L = f64x4`, `value` / `u_axis`
4777    /// pack the four rows' values / directions of primary `axis`.
4778    #[inline]
4779    pub fn seed_direction(value: L, axis: usize, u_axis: L) -> Self {
4780        OneSeedLane {
4781            base: Order2Lane::variable(value, axis),
4782            eps: Order2Lane::constant(u_axis),
4783        }
4784    }
4785
4786    /// The contracted-third channel after a `seed_direction(u)` evaluation:
4787    /// `out[a][b]` lane `i` is row `i`'s `Σ_c ℓ_{abc} u_c` (the ε-part Hessian).
4788    #[inline]
4789    #[must_use]
4790    pub fn contracted_third(&self) -> [[L; K]; K] {
4791        self.eps.h
4792    }
4793
4794    /// Lane-wise `self + o` (mirrors [`OneSeed::add`](crate::nested_dual::JetField::add)).
4795    #[inline]
4796    pub fn add(&self, o: &Self) -> Self {
4797        OneSeedLane {
4798            base: self.base.add(&o.base),
4799            eps: self.eps.add(&o.eps),
4800        }
4801    }
4802
4803    /// Lane-wise `self - o` (mirrors [`OneSeed::sub`](crate::nested_dual::JetField::sub)).
4804    #[inline]
4805    pub fn sub(&self, o: &Self) -> Self {
4806        OneSeedLane {
4807            base: self.base.sub(&o.base),
4808            eps: self.eps.sub(&o.eps),
4809        }
4810    }
4811
4812    /// Lane-wise `self · o`, ε² = 0 truncation (mirrors [`OneSeed::mul`](crate::nested_dual::JetField::mul)).
4813    #[inline]
4814    pub fn mul(&self, o: &Self) -> Self {
4815        let ab = &self.base;
4816        let ae = &self.eps;
4817        let bb = &o.base;
4818        let be = &o.eps;
4819        let mut eps = Order2Lane::constant(ab.v.mul(be.v).add(ae.v.mul(bb.v)));
4820        for i in 0..K {
4821            eps.g[i] =
4822                ab.v.mul(be.g[i])
4823                    .add(ab.g[i].mul(be.v))
4824                    .add(ae.v.mul(bb.g[i]))
4825                    .add(ae.g[i].mul(bb.v));
4826        }
4827        for i in 0..K {
4828            for j in i..K {
4829                let channel =
4830                    ab.v.mul(be.h[i][j])
4831                        .add(ab.g[i].mul(be.g[j]))
4832                        .add(ab.g[j].mul(be.g[i]))
4833                        .add(ab.h[i][j].mul(be.v))
4834                        .add(ae.v.mul(bb.h[i][j]))
4835                        .add(ae.g[i].mul(bb.g[j]))
4836                        .add(ae.g[j].mul(bb.g[i]))
4837                        .add(ae.h[i][j].mul(bb.v));
4838                eps.h[i][j] = channel;
4839                eps.h[j][i] = channel;
4840            }
4841        }
4842        OneSeedLane {
4843            base: self.base.mul(&o.base),
4844            eps,
4845        }
4846    }
4847
4848    /// Negate every part (mirrors [`OneSeed::neg`](crate::nested_dual::JetField::neg)).
4849    #[inline]
4850    pub fn neg(&self) -> Self {
4851        OneSeedLane {
4852            base: self.base.neg(),
4853            eps: self.eps.neg(),
4854        }
4855    }
4856
4857    /// Multiply every part by the plain scalar `s` (mirrors [`OneSeed::scale`](crate::nested_dual::JetField::scale)).
4858    #[inline]
4859    pub fn scale(&self, s: f64) -> Self {
4860        OneSeedLane {
4861            base: self.base.scale(s),
4862            eps: self.eps.scale(s),
4863        }
4864    }
4865
4866    /// Exact order-≤2-per-part Faà di Bruno composition `f ∘ self`, given the
4867    /// per-lane outer-derivative stack `d = [f, f′, f″, f‴, f⁗]`. Term-for-term
4868    /// identical to the fused [`OneSeed::compose_unary`](crate::nested_dual::JetField::compose_unary) channels: the base reads
4869    /// `d[0..=2]`, while the ε-coefficient directly evaluates
4870    /// `f′(base)·eps` from `d[1..=3]` without an intermediate jet.
4871    #[inline]
4872    pub fn compose_unary(&self, d: [L; 5]) -> Self {
4873        let base = self.base.compose_unary([d[0], d[1], d[2]]);
4874        let b = &self.base;
4875        let e = &self.eps;
4876        let mut eps = Order2Lane::constant(d[1].mul(e.v));
4877        for i in 0..K {
4878            eps.g[i] = d[2].mul(b.g[i]).mul(e.v).add(d[1].mul(e.g[i]));
4879        }
4880        for i in 0..K {
4881            for j in i..K {
4882                let mixed = b.g[i]
4883                    .mul(e.g[j])
4884                    .add(b.g[j].mul(e.g[i]))
4885                    .add(b.h[i][j].mul(e.v));
4886                let channel = d[1]
4887                    .mul(e.h[i][j])
4888                    .add(d[2].mul(mixed))
4889                    .add(d[3].mul(b.g[i]).mul(b.g[j]).mul(e.v));
4890                eps.h[i][j] = channel;
4891                eps.h[j][i] = channel;
4892            }
4893        }
4894        OneSeedLane { base, eps }
4895    }
4896
4897    /// `e^self`, per-lane stack `[e, e, e, e, e]` (matches [`JetScalar::exp`]).
4898    #[inline]
4899    pub fn exp(&self) -> Self {
4900        let d = self.base.v.unary5(|u| {
4901            let e = u.exp();
4902            [e, e, e, e, e]
4903        });
4904        self.compose_unary(d)
4905    }
4906
4907    /// `ln(self)`; caller guarantees positivity (matches [`JetScalar::ln`]).
4908    #[inline]
4909    pub fn ln(&self) -> Self {
4910        let d = self.base.v.unary5(|u| {
4911            let r = 1.0 / u;
4912            [u.ln(), r, -r * r, 2.0 * r * r * r, -6.0 * r * r * r * r]
4913        });
4914        self.compose_unary(d)
4915    }
4916
4917    /// `√self`; caller guarantees positivity (matches [`JetScalar::sqrt`]).
4918    #[inline]
4919    pub fn sqrt(&self) -> Self {
4920        let d = self.base.v.unary5(|u| {
4921            let s = u.sqrt();
4922            [
4923                s,
4924                0.5 / s,
4925                -0.25 / (u * s),
4926                0.375 / (u * u * s),
4927                -0.9375 / (u * u * u * s),
4928            ]
4929        });
4930        self.compose_unary(d)
4931    }
4932
4933    /// `1/self` (matches [`JetScalar::recip`]).
4934    #[inline]
4935    pub fn recip(&self) -> Self {
4936        let d = self.base.v.unary5(|u| {
4937            let r = 1.0 / u;
4938            let r2 = r * r;
4939            [r, -r2, 2.0 * r2 * r, -6.0 * r2 * r2, 24.0 * r2 * r2 * r]
4940        });
4941        self.compose_unary(d)
4942    }
4943
4944    /// `self^a` for real `a`; caller guarantees a positive base (matches
4945    /// [`JetScalar::powf`]).
4946    #[inline]
4947    pub fn powf(&self, a: f64) -> Self {
4948        let d = self.base.v.unary5(|u| {
4949            [
4950                u.powf(a),
4951                a * u.powf(a - 1.0),
4952                a * (a - 1.0) * u.powf(a - 2.0),
4953                a * (a - 1.0) * (a - 2.0) * u.powf(a - 3.0),
4954                a * (a - 1.0) * (a - 2.0) * (a - 3.0) * u.powf(a - 4.0),
4955            ]
4956        });
4957        self.compose_unary(d)
4958    }
4959
4960    /// `ln Γ(self)`; caller guarantees positivity (matches [`JetScalar::ln_gamma`],
4961    /// same hand-certified stack).
4962    #[inline]
4963    pub fn ln_gamma(&self) -> Self {
4964        let d = self
4965            .base
4966            .v
4967            .unary5(crate::jet_tower::ln_gamma_derivative_stack);
4968        self.compose_unary(d)
4969    }
4970
4971    /// `ψ(self)` digamma; caller guarantees positivity (matches
4972    /// [`JetScalar::digamma`], same hand-certified stack).
4973    #[inline]
4974    pub fn digamma(&self) -> Self {
4975        let d = self
4976            .base
4977            .v
4978            .unary5(crate::jet_tower::digamma_derivative_stack);
4979        self.compose_unary(d)
4980    }
4981}
4982
4983impl<const K: usize> OneSeedBatch<K> {
4984    /// Extract lane `i`'s parts as a production [`OneSeed<K>`]. Lane `i` is
4985    /// `to_bits`-identical to evaluating the same program at [`OneSeed<K>`] on
4986    /// row `i` (see `batch_tests`).
4987    #[inline]
4988    #[must_use]
4989    pub fn lane(&self, i: usize) -> OneSeed<K> {
4990        OneSeed {
4991            base: self.base.lane(i),
4992            eps: self.eps.lane(i),
4993        }
4994    }
4995}
4996
4997// ── TwoSeed<K>: two-seed, contracted fourth (doc §A.3) ──────────────────
4998
4999/// Two-seed scalar: an [`Order2`] base plus TWO nilpotents ε, δ
5000/// (`ε² = δ² = 0`, `εδ` retained) — four [`Order2`] parts
5001/// `s = base + ε·eps + δ·del + εδ·eps_del`.
5002///
5003/// Product truncates `ε² = δ² = 0` (doc §A.3): each part is built from
5004/// [`Order2`] products of the four input parts. Composition picks up
5005/// successively higher outer derivatives, the cross part carrying the second
5006/// Faà di Bruno term `f''·eps·del + f'·eps_del`.
5007///
5008/// Seed each primary with [`seed`](Self::seed): base = `variable(x, axis)`,
5009/// eps = `constant(u_axis)`, del = `constant(v_axis)`, eps_del = `constant(0)`.
5010/// Then the εδ-component of the evaluated Hessian channel is the contracted
5011/// fourth `[eps_del.h][a][b] = Σ_{cd} ℓ_{abcd} u_c v_d` — exactly
5012/// `row_fourth_contracted(u, v)`, without materialising `t4`.
5013#[derive(Clone, Copy, Debug)]
5014pub struct TwoSeed<const K: usize> {
5015    /// The `ε⁰δ⁰` part: value / grad / Hessian of `ℓ`.
5016    pub base: Order2<K>,
5017    /// The `ε¹δ⁰` part.
5018    pub eps: Order2<K>,
5019    /// The `ε⁰δ¹` part.
5020    pub del: Order2<K>,
5021    /// The `ε¹δ¹` part. After a `seed(u, v)` evaluation,
5022    /// `eps_del.h[a][b] = Σ_{cd} ℓ_{abcd} u_c v_d`.
5023    pub eps_del: Order2<K>,
5024}
5025
5026impl<const K: usize> TwoSeed<K> {
5027    /// Seed primary `axis` at value `x` with ε-direction `u_axis` and
5028    /// δ-direction `v_axis`:
5029    /// `p_axis = p_axis⁰ + x-seed + ε·u_axis + δ·v_axis` (doc §A.3 "Seeding").
5030    pub fn seed(x: f64, axis: usize, u_axis: f64, v_axis: f64) -> Self {
5031        TwoSeed {
5032            base: Order2::variable(x, axis),
5033            eps: Order2::constant(u_axis),
5034            del: Order2::constant(v_axis),
5035            eps_del: Order2::constant(0.0),
5036        }
5037    }
5038
5039    /// The contracted-fourth channel after a `seed(u, v)` evaluation:
5040    /// `out[a][b] = Σ_{cd} ℓ_{abcd} u_c v_d`, i.e. the εδ-coefficient's Hessian.
5041    pub fn contracted_fourth(&self) -> [[f64; K]; K] {
5042        *self.eps_del.h()
5043    }
5044}
5045
5046impl<const K: usize> JetScalar<K> for TwoSeed<K> {
5047    fn constant(c: f64) -> Self {
5048        TwoSeed {
5049            base: Order2::constant(c),
5050            eps: Order2::constant(0.0),
5051            del: Order2::constant(0.0),
5052            eps_del: Order2::constant(0.0),
5053        }
5054    }
5055    fn variable(x: f64, axis: usize) -> Self {
5056        TwoSeed {
5057            base: Order2::variable(x, axis),
5058            eps: Order2::constant(0.0),
5059            del: Order2::constant(0.0),
5060            eps_del: Order2::constant(0.0),
5061        }
5062    }
5063}
5064
5065impl<const K: usize> crate::nested_dual::JetField for TwoSeed<K> {
5066    fn value(&self) -> f64 {
5067        self.base.value()
5068    }
5069    fn add(&self, o: &Self) -> Self {
5070        TwoSeed {
5071            base: self.base.add(&o.base),
5072            eps: self.eps.add(&o.eps),
5073            del: self.del.add(&o.del),
5074            eps_del: self.eps_del.add(&o.eps_del),
5075        }
5076    }
5077    fn sub(&self, o: &Self) -> Self {
5078        TwoSeed {
5079            base: self.base.sub(&o.base),
5080            eps: self.eps.sub(&o.eps),
5081            del: self.del.sub(&o.del),
5082            eps_del: self.eps_del.sub(&o.eps_del),
5083        }
5084    }
5085    fn mul(&self, o: &Self) -> Self {
5086        let a = self;
5087        let b = o;
5088        // Truncate ε² = δ² = 0 (doc §A.3 product table).
5089        let base = a.base.mul(&b.base);
5090        let eps = a.base.mul(&b.eps).add(&a.eps.mul(&b.base));
5091        let del = a.base.mul(&b.del).add(&a.del.mul(&b.base));
5092        let eps_del = a
5093            .base
5094            .mul(&b.eps_del)
5095            .add(&a.eps.mul(&b.del))
5096            .add(&a.del.mul(&b.eps))
5097            .add(&a.eps_del.mul(&b.base));
5098        TwoSeed {
5099            base,
5100            eps,
5101            del,
5102            eps_del,
5103        }
5104    }
5105    fn neg(&self) -> Self {
5106        TwoSeed {
5107            base: self.base.neg(),
5108            eps: self.eps.neg(),
5109            del: self.del.neg(),
5110            eps_del: self.eps_del.neg(),
5111        }
5112    }
5113    fn scale(&self, s: f64) -> Self {
5114        TwoSeed {
5115            base: self.base.scale(s),
5116            eps: self.eps.scale(s),
5117            del: self.del.scale(s),
5118            eps_del: self.eps_del.scale(s),
5119        }
5120    }
5121    fn compose_unary(&self, d: [f64; 5]) -> Self {
5122        // f(s) with s = base + ε eps + δ del + εδ eps_del, ε²=δ²=0:
5123        //   f(s) = f(base)
5124        //        + ε · f'(base)·eps
5125        //        + δ · f'(base)·del
5126        //        + εδ · ( f''(base)·eps·del + f'(base)·eps_del ).
5127        // Each f^{(r)}(base) is the Order2 composition of base with the stack
5128        // shifted r entries (doc §A.3 composition). Order2 reads only the
5129        // leading three entries of whatever stack it is handed, so the trailing
5130        // padding slots are unused (the fixed-length array makes this total).
5131        let base = self.base.compose_unary([d[0], d[1], d[2], d[3], d[4]]);
5132        let fprime = self.base.compose_unary([d[1], d[2], d[3], d[4], d[4]]); // f'(base) as Order2
5133        let fsecond = self.base.compose_unary([d[2], d[3], d[4], d[4], d[4]]); // f''(base) as Order2
5134        let eps = fprime.mul(&self.eps);
5135        let del = fprime.mul(&self.del);
5136        let eps_del = fsecond
5137            .mul(&self.eps)
5138            .mul(&self.del)
5139            .add(&fprime.mul(&self.eps_del));
5140        TwoSeed {
5141            base,
5142            eps,
5143            del,
5144            eps_del,
5145        }
5146    }
5147}
5148
5149// ── TwoSeedLane<L, K>: lane-batched two-seed, contracted fourth (doc §A.3) ─
5150
5151/// Lane-batched [`TwoSeed`]: the same two-seed scalar with its four [`Order2`]
5152/// parts re-typed to [`Order2Lane<L, K>`], so one `L = f64x4` instance carries
5153/// FOUR rows' contracted-fourth evaluations per vector pass.
5154///
5155/// Every operation is a term-for-term structural re-type of the scalar
5156/// [`TwoSeed`] ops onto the lane-implemented [`Order2Lane`] algebra. With
5157/// `L = f64`, `TwoSeedLane<f64, K>` is `to_bits`-identical to [`TwoSeed<K>`];
5158/// with `L = f64x4`, lane `i` is `to_bits`-identical to that (see `batch_tests`).
5159#[derive(Clone, Copy, Debug)]
5160pub struct TwoSeedLane<L: Lane, const K: usize> {
5161    /// The `ε⁰δ⁰` part.
5162    pub base: Order2Lane<L, K>,
5163    /// The `ε¹δ⁰` part.
5164    pub eps: Order2Lane<L, K>,
5165    /// The `ε⁰δ¹` part.
5166    pub del: Order2Lane<L, K>,
5167    /// The `ε¹δ¹` part. After a `seed(u, v)` evaluation, `eps_del.h[a][b]`
5168    /// lane `i` is row `i`'s `Σ_{cd} ℓ_{abcd} u_c v_d`.
5169    pub eps_del: Order2Lane<L, K>,
5170}
5171
5172/// The 4-rows-per-pass batched two-seed scalar (`wide::f64x4` lanes).
5173pub type TwoSeedBatch<const K: usize> = TwoSeedLane<wide::f64x4, K>;
5174
5175impl<L: Lane, const K: usize> TwoSeedLane<L, K> {
5176    /// A constant: base = `constant(c)`, all seed parts zero (mirrors
5177    /// [`TwoSeed::constant`]).
5178    #[inline]
5179    pub fn constant(c: L) -> Self {
5180        let z = Order2Lane::constant(L::splat(0.0));
5181        TwoSeedLane {
5182            base: Order2Lane::constant(c),
5183            eps: z,
5184            del: z,
5185            eps_del: z,
5186        }
5187    }
5188
5189    /// The seeded variable `p_axis` at (per-lane) value `value`, no ε/δ direction
5190    /// (mirrors [`TwoSeed::variable`]).
5191    #[inline]
5192    pub fn variable(value: L, axis: usize) -> Self {
5193        let z = Order2Lane::constant(L::splat(0.0));
5194        TwoSeedLane {
5195            base: Order2Lane::variable(value, axis),
5196            eps: z,
5197            del: z,
5198            eps_del: z,
5199        }
5200    }
5201
5202    /// Seed primary `axis` at (per-lane) value `value` with ε-direction `u_axis`
5203    /// and δ-direction `v_axis` (mirrors [`TwoSeed::seed`]). With `L = f64x4`,
5204    /// each argument packs the four rows' values for primary `axis`.
5205    #[inline]
5206    pub fn seed(value: L, axis: usize, u_axis: L, v_axis: L) -> Self {
5207        TwoSeedLane {
5208            base: Order2Lane::variable(value, axis),
5209            eps: Order2Lane::constant(u_axis),
5210            del: Order2Lane::constant(v_axis),
5211            eps_del: Order2Lane::constant(L::splat(0.0)),
5212        }
5213    }
5214
5215    /// The contracted-fourth channel after a `seed(u, v)` evaluation:
5216    /// `out[a][b]` lane `i` is row `i`'s `Σ_{cd} ℓ_{abcd} u_c v_d`
5217    /// (the εδ-part Hessian).
5218    #[inline]
5219    #[must_use]
5220    pub fn contracted_fourth(&self) -> [[L; K]; K] {
5221        self.eps_del.h
5222    }
5223
5224    /// Lane-wise `self + o` (mirrors [`TwoSeed::add`](crate::nested_dual::JetField::add)).
5225    #[inline]
5226    pub fn add(&self, o: &Self) -> Self {
5227        TwoSeedLane {
5228            base: self.base.add(&o.base),
5229            eps: self.eps.add(&o.eps),
5230            del: self.del.add(&o.del),
5231            eps_del: self.eps_del.add(&o.eps_del),
5232        }
5233    }
5234
5235    /// Lane-wise `self - o` (mirrors [`TwoSeed::sub`](crate::nested_dual::JetField::sub)).
5236    #[inline]
5237    pub fn sub(&self, o: &Self) -> Self {
5238        TwoSeedLane {
5239            base: self.base.sub(&o.base),
5240            eps: self.eps.sub(&o.eps),
5241            del: self.del.sub(&o.del),
5242            eps_del: self.eps_del.sub(&o.eps_del),
5243        }
5244    }
5245
5246    /// Lane-wise `self · o`, ε² = δ² = 0 truncation (mirrors [`TwoSeed::mul`](crate::nested_dual::JetField::mul)).
5247    #[inline]
5248    pub fn mul(&self, o: &Self) -> Self {
5249        let a = self;
5250        let b = o;
5251        let base = a.base.mul(&b.base);
5252        let eps = a.base.mul(&b.eps).add(&a.eps.mul(&b.base));
5253        let del = a.base.mul(&b.del).add(&a.del.mul(&b.base));
5254        let eps_del = a
5255            .base
5256            .mul(&b.eps_del)
5257            .add(&a.eps.mul(&b.del))
5258            .add(&a.del.mul(&b.eps))
5259            .add(&a.eps_del.mul(&b.base));
5260        TwoSeedLane {
5261            base,
5262            eps,
5263            del,
5264            eps_del,
5265        }
5266    }
5267
5268    /// Negate every part (mirrors [`TwoSeed::neg`](crate::nested_dual::JetField::neg)).
5269    #[inline]
5270    pub fn neg(&self) -> Self {
5271        TwoSeedLane {
5272            base: self.base.neg(),
5273            eps: self.eps.neg(),
5274            del: self.del.neg(),
5275            eps_del: self.eps_del.neg(),
5276        }
5277    }
5278
5279    /// Multiply every part by the plain scalar `s` (mirrors [`TwoSeed::scale`](crate::nested_dual::JetField::scale)).
5280    #[inline]
5281    pub fn scale(&self, s: f64) -> Self {
5282        TwoSeedLane {
5283            base: self.base.scale(s),
5284            eps: self.eps.scale(s),
5285            del: self.del.scale(s),
5286            eps_del: self.eps_del.scale(s),
5287        }
5288    }
5289
5290    /// Exact composition `f ∘ self`, given the per-lane outer-derivative stack
5291    /// `d = [f, f′, f″, f‴, f⁗]`. Term-for-term identical to
5292    /// [`TwoSeed::compose_unary`](crate::nested_dual::JetField::compose_unary): base reads `d[0..=2]`, `f′(base)` reads
5293    /// `d[1..=3]`, `f″(base)` reads `d[2..=4]`, and the cross part carries
5294    /// `f″·eps·del + f′·eps_del`.
5295    #[inline]
5296    pub fn compose_unary(&self, d: [L; 5]) -> Self {
5297        let base = self.base.compose_unary([d[0], d[1], d[2]]);
5298        let fprime = self.base.compose_unary([d[1], d[2], d[3]]);
5299        let fsecond = self.base.compose_unary([d[2], d[3], d[4]]);
5300        let eps = fprime.mul(&self.eps);
5301        let del = fprime.mul(&self.del);
5302        let eps_del = fsecond
5303            .mul(&self.eps)
5304            .mul(&self.del)
5305            .add(&fprime.mul(&self.eps_del));
5306        TwoSeedLane {
5307            base,
5308            eps,
5309            del,
5310            eps_del,
5311        }
5312    }
5313
5314    /// `e^self`, per-lane stack `[e; 5]` (matches [`JetScalar::exp`]).
5315    #[inline]
5316    pub fn exp(&self) -> Self {
5317        let d = self.base.v.unary5(|u| {
5318            let e = u.exp();
5319            [e, e, e, e, e]
5320        });
5321        self.compose_unary(d)
5322    }
5323
5324    /// `ln(self)`; caller guarantees positivity (matches [`JetScalar::ln`]).
5325    #[inline]
5326    pub fn ln(&self) -> Self {
5327        let d = self.base.v.unary5(|u| {
5328            let r = 1.0 / u;
5329            [u.ln(), r, -r * r, 2.0 * r * r * r, -6.0 * r * r * r * r]
5330        });
5331        self.compose_unary(d)
5332    }
5333
5334    /// `√self`; caller guarantees positivity (matches [`JetScalar::sqrt`]).
5335    #[inline]
5336    pub fn sqrt(&self) -> Self {
5337        let d = self.base.v.unary5(|u| {
5338            let s = u.sqrt();
5339            [
5340                s,
5341                0.5 / s,
5342                -0.25 / (u * s),
5343                0.375 / (u * u * s),
5344                -0.9375 / (u * u * u * s),
5345            ]
5346        });
5347        self.compose_unary(d)
5348    }
5349
5350    /// `1/self` (matches [`JetScalar::recip`]).
5351    #[inline]
5352    pub fn recip(&self) -> Self {
5353        let d = self.base.v.unary5(|u| {
5354            let r = 1.0 / u;
5355            let r2 = r * r;
5356            [r, -r2, 2.0 * r2 * r, -6.0 * r2 * r2, 24.0 * r2 * r2 * r]
5357        });
5358        self.compose_unary(d)
5359    }
5360
5361    /// `self^a` for real `a`; caller guarantees a positive base (matches
5362    /// [`JetScalar::powf`]).
5363    #[inline]
5364    pub fn powf(&self, a: f64) -> Self {
5365        let d = self.base.v.unary5(|u| {
5366            [
5367                u.powf(a),
5368                a * u.powf(a - 1.0),
5369                a * (a - 1.0) * u.powf(a - 2.0),
5370                a * (a - 1.0) * (a - 2.0) * u.powf(a - 3.0),
5371                a * (a - 1.0) * (a - 2.0) * (a - 3.0) * u.powf(a - 4.0),
5372            ]
5373        });
5374        self.compose_unary(d)
5375    }
5376
5377    /// `ln Γ(self)`; caller guarantees positivity (matches [`JetScalar::ln_gamma`]).
5378    #[inline]
5379    pub fn ln_gamma(&self) -> Self {
5380        let d = self
5381            .base
5382            .v
5383            .unary5(crate::jet_tower::ln_gamma_derivative_stack);
5384        self.compose_unary(d)
5385    }
5386
5387    /// `ψ(self)` digamma; caller guarantees positivity (matches
5388    /// [`JetScalar::digamma`]).
5389    #[inline]
5390    pub fn digamma(&self) -> Self {
5391        let d = self
5392            .base
5393            .v
5394            .unary5(crate::jet_tower::digamma_derivative_stack);
5395        self.compose_unary(d)
5396    }
5397}
5398
5399impl<const K: usize> TwoSeedBatch<K> {
5400    /// Extract lane `i`'s parts as a production [`TwoSeed<K>`]. Lane `i` is
5401    /// `to_bits`-identical to evaluating the same program at [`TwoSeed<K>`] on
5402    /// row `i` (see `batch_tests`).
5403    #[inline]
5404    #[must_use]
5405    pub fn lane(&self, i: usize) -> TwoSeed<K> {
5406        TwoSeed {
5407            base: self.base.lane(i),
5408            eps: self.eps.lane(i),
5409            del: self.del.lane(i),
5410            eps_del: self.eps_del.lane(i),
5411        }
5412    }
5413}
5414
5415// ── Tower3<K>: value / gradient / Hessian / third tensor ────────────────
5416
5417/// The order-≤3 [`crate::jet_tower::Tower3`] is also a [`JetScalar`]. It serves
5418/// consumers that read `.t3` but never `.t4`, avoiding the fourth-tensor
5419/// product/composition work while preserving the lower channels
5420/// bit-for-bit against [`crate::jet_tower::Tower4`].
5421impl<const K: usize> JetScalar<K> for crate::jet_tower::Tower3<K> {
5422    fn constant(c: f64) -> Self {
5423        crate::jet_tower::Tower3::constant(c)
5424    }
5425    fn variable(x: f64, axis: usize) -> Self {
5426        crate::jet_tower::Tower3::variable(x, axis)
5427    }
5428}
5429
5430impl<const K: usize> crate::nested_dual::JetField for crate::jet_tower::Tower3<K> {
5431    fn value(&self) -> f64 {
5432        self.v
5433    }
5434    fn add(&self, o: &Self) -> Self {
5435        *self + *o
5436    }
5437    fn sub(&self, o: &Self) -> Self {
5438        *self + o.scale(-1.0)
5439    }
5440    fn mul(&self, o: &Self) -> Self {
5441        crate::jet_tower::Tower3::mul(self, o)
5442    }
5443    fn neg(&self) -> Self {
5444        self.scale(-1.0)
5445    }
5446    fn scale(&self, s: f64) -> Self {
5447        crate::jet_tower::Tower3::scale(self, s)
5448    }
5449    fn compose_unary(&self, d: [f64; 5]) -> Self {
5450        crate::jet_tower::Tower3::compose_unary(self, [d[0], d[1], d[2], d[3]])
5451    }
5452}
5453
5454// ── Tower4<K>: full dense tower as a JetScalar (the all-channels scalar) ─
5455
5456/// The full dense [`crate::jet_tower::Tower4`] is itself a [`JetScalar`]: it
5457/// carries EVERY channel, so a row expression written ONCE against [`JetScalar`]
5458/// can be evaluated at `Tower4` to obtain the full `(v, g, H, t3, t4)` in one
5459/// pass. This is BOTH the #932 oracle ground truth the packed [`Order2`] /
5460/// [`OneSeed`] / [`TwoSeed`] scalars are pinned against, AND a production scalar:
5461/// a family whose uncontracted third / fourth derivative tensors are needed
5462/// (the BMS rigid `third_full` / `fourth_full` caches) evaluates the SAME
5463/// generic row-NLL expression at `Tower4` and reads `.t3` / `.t4` off the
5464/// result — so the dense tensors come from the single source of truth, not a
5465/// separately hand-written jet. The packed scalars serve the consumers that
5466/// need only `(v, g, H)` (`Order2`) or one / two contractions
5467/// (`OneSeed` / `TwoSeed`) without paying for the dense tensors.
5468impl<const K: usize> JetScalar<K> for crate::jet_tower::Tower4<K> {
5469    fn constant(c: f64) -> Self {
5470        crate::jet_tower::Tower4::constant(c)
5471    }
5472    fn variable(x: f64, axis: usize) -> Self {
5473        crate::jet_tower::Tower4::variable(x, axis)
5474    }
5475}
5476
5477impl<const K: usize> crate::nested_dual::JetField for crate::jet_tower::Tower4<K> {
5478    fn value(&self) -> f64 {
5479        self.v
5480    }
5481    fn add(&self, o: &Self) -> Self {
5482        *self + *o
5483    }
5484    fn sub(&self, o: &Self) -> Self {
5485        *self - *o
5486    }
5487    fn mul(&self, o: &Self) -> Self {
5488        crate::jet_tower::Tower4::mul(self, o)
5489    }
5490    fn neg(&self) -> Self {
5491        self.scale(-1.0)
5492    }
5493    fn scale(&self, s: f64) -> Self {
5494        crate::jet_tower::Tower4::scale(self, s)
5495    }
5496    fn compose_unary(&self, d: [f64; 5]) -> Self {
5497        crate::jet_tower::Tower4::compose_unary(self, d)
5498    }
5499}
5500
5501#[cfg(test)]
5502mod tests {
5503    use super::*;
5504    use crate::jet_tower::{RowProgram, Tower4, program_full_tower};
5505    use crate::nested_dual::JetField;
5506
5507    struct DenseSymmetric3([[f64; 3]; 3]);
5508
5509    impl SymmetricQuadraticCoefficients for DenseSymmetric3 {
5510        fn dimension(&self) -> usize {
5511            3
5512        }
5513
5514        fn multiply(&self, input: &[f64], output: &mut [f64]) {
5515            assert_eq!(input.len(), 3);
5516            assert_eq!(output.len(), 3);
5517            for (row, output) in output.iter_mut().enumerate() {
5518                *output = (0..3)
5519                    .map(|column| self.0[row][column] * input[column])
5520                    .sum();
5521            }
5522        }
5523
5524        fn coefficient(&self, row: usize, column: usize) -> f64 {
5525            self.0[row][column]
5526        }
5527    }
5528
5529    #[test]
5530    fn symmetric_quadratic_order2_lowerings_match_scalar_program() {
5531        const K: usize = 4;
5532        let coefficients = DenseSymmetric3([[1.2, 0.3, -0.2], [0.3, 0.8, 0.15], [-0.2, 0.15, 1.5]]);
5533        let values = [0.4, -0.7, 1.1, 0.25];
5534        let fixed_vars: [Order2<K>; K] =
5535            std::array::from_fn(|axis| Order2::variable(values[axis], axis));
5536        let fixed_inputs = [
5537            fixed_vars[0].mul(&fixed_vars[1]).add(&fixed_vars[3]),
5538            fixed_vars[1].exp().add(&fixed_vars[2].scale(0.4)),
5539            fixed_vars[2].mul(&fixed_vars[2]).sub(&fixed_vars[0]),
5540        ];
5541        let fixed_direct = Order2::symmetric_quadratic_form(&fixed_inputs, &coefficients);
5542        let fixed_scalar = symmetric_quadratic_form_default(
5543            &fixed_inputs,
5544            &coefficients,
5545            Order2::constant,
5546            JetField::add,
5547            JetField::mul,
5548            JetField::scale,
5549        );
5550        let weights = [0.7, -1.1, 0.35];
5551        let fixed_linear_direct = Order2::linear_combination(&fixed_inputs, &weights);
5552        let fixed_linear_scalar = linear_combination_default(
5553            &fixed_inputs,
5554            &weights,
5555            Order2::constant,
5556            JetField::add,
5557            JetField::scale,
5558        );
5559        let derivative_stacks = [
5560            [0.8, -0.3, 0.7, 0.0, 0.0],
5561            [-0.2, 1.1, -0.4, 0.0, 0.0],
5562            [1.4, 0.25, 0.6, 0.0, 0.0],
5563        ];
5564        let fixed_add_direct = fixed_inputs[0].add_constant(0.65);
5565        let fixed_add_scalar = fixed_inputs[0].add(&Order2::constant(0.65));
5566        let fixed_multiply_add_direct =
5567            fixed_inputs[0].multiply_add(&fixed_inputs[1], &fixed_inputs[2]);
5568        let fixed_multiply_add_scalar = multiply_add_default(
5569            &fixed_inputs[0],
5570            &fixed_inputs[1],
5571            &fixed_inputs[2],
5572            JetField::mul,
5573            JetField::add,
5574        );
5575        let fixed_composed_direct = Order2::composed_sum(&fixed_inputs, &derivative_stacks);
5576        let fixed_composed_scalar = composed_sum_default(
5577            &fixed_inputs,
5578            &derivative_stacks,
5579            Order2::constant,
5580            JetField::add,
5581            JetField::compose_unary,
5582        );
5583
5584        let value_vars: [RuntimeValue; K] =
5585            std::array::from_fn(|axis| RuntimeValue::variable(values[axis], axis, K, &()));
5586        let value_inputs = [
5587            value_vars[0].mul(&value_vars[1]).add(&value_vars[3]),
5588            value_vars[1].exp().add(&value_vars[2].scale(0.4)),
5589            value_vars[2].mul(&value_vars[2]).sub(&value_vars[0]),
5590        ];
5591        let value_quadratic =
5592            RuntimeValue::symmetric_quadratic_form(&value_inputs, &coefficients, K, &());
5593        let value_linear = RuntimeValue::linear_combination(&value_inputs, &weights, K, &());
5594        let value_composed = RuntimeValue::composed_sum(&value_inputs, &derivative_stacks, K, &());
5595
5596        let arena = DynamicJetArena::new();
5597        let dynamic_vars: [DynamicOrder2<'_>; K] =
5598            std::array::from_fn(|axis| DynamicOrder2::variable(values[axis], axis, K, &arena));
5599        let dynamic_inputs = [
5600            dynamic_vars[0].mul(&dynamic_vars[1]).add(&dynamic_vars[3]),
5601            dynamic_vars[1].exp().add(&dynamic_vars[2].scale(0.4)),
5602            dynamic_vars[2].mul(&dynamic_vars[2]).sub(&dynamic_vars[0]),
5603        ];
5604        let dynamic_direct =
5605            DynamicOrder2::symmetric_quadratic_form(&dynamic_inputs, &coefficients, K, &arena);
5606        let dynamic_scalar = symmetric_quadratic_form_default(
5607            &dynamic_inputs,
5608            &coefficients,
5609            |value| DynamicOrder2::constant(value, K, &arena),
5610            RuntimeJetScalar::add,
5611            RuntimeJetScalar::mul,
5612            RuntimeJetScalar::scale,
5613        );
5614        let dynamic_linear_direct =
5615            DynamicOrder2::linear_combination(&dynamic_inputs, &weights, K, &arena);
5616        let dynamic_linear_scalar = linear_combination_default(
5617            &dynamic_inputs,
5618            &weights,
5619            |value| DynamicOrder2::constant(value, K, &arena),
5620            RuntimeJetScalar::add,
5621            RuntimeJetScalar::scale,
5622        );
5623        let dynamic_add_direct = dynamic_inputs[0].add_constant(0.65);
5624        let dynamic_add_scalar = dynamic_inputs[0].add(&DynamicOrder2::constant(0.65, K, &arena));
5625        let dynamic_multiply_add_direct =
5626            dynamic_inputs[0].multiply_add(&dynamic_inputs[1], &dynamic_inputs[2]);
5627        let dynamic_multiply_add_scalar = multiply_add_default(
5628            &dynamic_inputs[0],
5629            &dynamic_inputs[1],
5630            &dynamic_inputs[2],
5631            RuntimeJetScalar::mul,
5632            RuntimeJetScalar::add,
5633        );
5634        let dynamic_composed_direct =
5635            DynamicOrder2::composed_sum(&dynamic_inputs, &derivative_stacks, K, &arena);
5636        let dynamic_composed_scalar = composed_sum_default(
5637            &dynamic_inputs,
5638            &derivative_stacks,
5639            |value| DynamicOrder2::constant(value, K, &arena),
5640            RuntimeJetScalar::add,
5641            RuntimeJetScalar::compose_unary,
5642        );
5643
5644        let tolerance = 2.0e-13;
5645        for (label, actual, expected) in [
5646            ("fixed value", fixed_direct.value(), fixed_scalar.value()),
5647            (
5648                "zero-order quadratic value",
5649                value_quadratic.value(),
5650                fixed_direct.value(),
5651            ),
5652            (
5653                "zero-order linear value",
5654                value_linear.value(),
5655                fixed_linear_direct.value(),
5656            ),
5657            (
5658                "zero-order composed value",
5659                value_composed.value(),
5660                fixed_composed_direct.value(),
5661            ),
5662            (
5663                "dynamic value",
5664                dynamic_direct.value(),
5665                dynamic_scalar.value(),
5666            ),
5667            (
5668                "fixed linear value",
5669                fixed_linear_direct.value(),
5670                fixed_linear_scalar.value(),
5671            ),
5672            (
5673                "dynamic linear value",
5674                dynamic_linear_direct.value(),
5675                dynamic_linear_scalar.value(),
5676            ),
5677            (
5678                "fixed add-constant value",
5679                fixed_add_direct.value(),
5680                fixed_add_scalar.value(),
5681            ),
5682            (
5683                "dynamic add-constant value",
5684                dynamic_add_direct.value(),
5685                dynamic_add_scalar.value(),
5686            ),
5687            (
5688                "fixed multiply-add value",
5689                fixed_multiply_add_direct.value(),
5690                fixed_multiply_add_scalar.value(),
5691            ),
5692            (
5693                "dynamic multiply-add value",
5694                dynamic_multiply_add_direct.value(),
5695                dynamic_multiply_add_scalar.value(),
5696            ),
5697            (
5698                "fixed composed-sum value",
5699                fixed_composed_direct.value(),
5700                fixed_composed_scalar.value(),
5701            ),
5702            (
5703                "dynamic composed-sum value",
5704                dynamic_composed_direct.value(),
5705                dynamic_composed_scalar.value(),
5706            ),
5707        ] {
5708            assert!(
5709                (actual - expected).abs() <= tolerance * actual.abs().max(expected.abs()).max(1.0),
5710                "{label}: direct={actual:+.16e}, scalar={expected:+.16e}"
5711            );
5712        }
5713        for primary_a in 0..K {
5714            for (label, actual, expected) in [
5715                (
5716                    "fixed gradient",
5717                    fixed_direct.g()[primary_a],
5718                    fixed_scalar.g()[primary_a],
5719                ),
5720                (
5721                    "dynamic gradient",
5722                    dynamic_direct.g()[primary_a],
5723                    dynamic_scalar.g()[primary_a],
5724                ),
5725                (
5726                    "fixed linear gradient",
5727                    fixed_linear_direct.g()[primary_a],
5728                    fixed_linear_scalar.g()[primary_a],
5729                ),
5730                (
5731                    "dynamic linear gradient",
5732                    dynamic_linear_direct.g()[primary_a],
5733                    dynamic_linear_scalar.g()[primary_a],
5734                ),
5735                (
5736                    "fixed add-constant gradient",
5737                    fixed_add_direct.g()[primary_a],
5738                    fixed_add_scalar.g()[primary_a],
5739                ),
5740                (
5741                    "dynamic add-constant gradient",
5742                    dynamic_add_direct.g()[primary_a],
5743                    dynamic_add_scalar.g()[primary_a],
5744                ),
5745                (
5746                    "fixed multiply-add gradient",
5747                    fixed_multiply_add_direct.g()[primary_a],
5748                    fixed_multiply_add_scalar.g()[primary_a],
5749                ),
5750                (
5751                    "dynamic multiply-add gradient",
5752                    dynamic_multiply_add_direct.g()[primary_a],
5753                    dynamic_multiply_add_scalar.g()[primary_a],
5754                ),
5755                (
5756                    "fixed composed-sum gradient",
5757                    fixed_composed_direct.g()[primary_a],
5758                    fixed_composed_scalar.g()[primary_a],
5759                ),
5760                (
5761                    "dynamic composed-sum gradient",
5762                    dynamic_composed_direct.g()[primary_a],
5763                    dynamic_composed_scalar.g()[primary_a],
5764                ),
5765            ] {
5766                assert!(
5767                    (actual - expected).abs()
5768                        <= tolerance * actual.abs().max(expected.abs()).max(1.0),
5769                    "{label}[{primary_a}]: direct={actual:+.16e}, scalar={expected:+.16e}"
5770                );
5771            }
5772            for primary_b in 0..K {
5773                for (label, actual, expected) in [
5774                    (
5775                        "fixed Hessian",
5776                        fixed_direct.h()[primary_a][primary_b],
5777                        fixed_scalar.h()[primary_a][primary_b],
5778                    ),
5779                    (
5780                        "dynamic Hessian",
5781                        dynamic_direct.h_at(primary_a, primary_b),
5782                        dynamic_scalar.h_at(primary_a, primary_b),
5783                    ),
5784                    (
5785                        "fixed linear Hessian",
5786                        fixed_linear_direct.h()[primary_a][primary_b],
5787                        fixed_linear_scalar.h()[primary_a][primary_b],
5788                    ),
5789                    (
5790                        "dynamic linear Hessian",
5791                        dynamic_linear_direct.h_at(primary_a, primary_b),
5792                        dynamic_linear_scalar.h_at(primary_a, primary_b),
5793                    ),
5794                    (
5795                        "fixed add-constant Hessian",
5796                        fixed_add_direct.h()[primary_a][primary_b],
5797                        fixed_add_scalar.h()[primary_a][primary_b],
5798                    ),
5799                    (
5800                        "dynamic add-constant Hessian",
5801                        dynamic_add_direct.h_at(primary_a, primary_b),
5802                        dynamic_add_scalar.h_at(primary_a, primary_b),
5803                    ),
5804                    (
5805                        "fixed multiply-add Hessian",
5806                        fixed_multiply_add_direct.h()[primary_a][primary_b],
5807                        fixed_multiply_add_scalar.h()[primary_a][primary_b],
5808                    ),
5809                    (
5810                        "dynamic multiply-add Hessian",
5811                        dynamic_multiply_add_direct.h_at(primary_a, primary_b),
5812                        dynamic_multiply_add_scalar.h_at(primary_a, primary_b),
5813                    ),
5814                    (
5815                        "fixed composed-sum Hessian",
5816                        fixed_composed_direct.h()[primary_a][primary_b],
5817                        fixed_composed_scalar.h()[primary_a][primary_b],
5818                    ),
5819                    (
5820                        "dynamic composed-sum Hessian",
5821                        dynamic_composed_direct.h_at(primary_a, primary_b),
5822                        dynamic_composed_scalar.h_at(primary_a, primary_b),
5823                    ),
5824                ] {
5825                    assert!(
5826                        (actual - expected).abs()
5827                            <= tolerance * actual.abs().max(expected.abs()).max(1.0),
5828                        "{label}[{primary_a},{primary_b}]: direct={actual:+.16e}, scalar={expected:+.16e}"
5829                    );
5830                }
5831            }
5832        }
5833    }
5834
5835    #[test]
5836    fn compiled_product_affine_and_fused_nodes_match_scalar_program_randomized() {
5837        const K: usize = 4;
5838        const TERMS: usize = 10;
5839
5840        fn sample(state: &mut u64) -> f64 {
5841            *state ^= *state << 13;
5842            *state ^= *state >> 7;
5843            *state ^= *state << 17;
5844            let unit = (*state >> 11) as f64 * (1.0 / ((1_u64 << 53) as f64));
5845            2.0 * unit - 1.0
5846        }
5847
5848        fn arbitrary_order2<const K: usize>(state: &mut u64) -> Order2<K> {
5849            let mut tower = crate::jet_tower::Tower2::zero();
5850            tower.v = sample(state);
5851            for primary in 0..K {
5852                tower.g[primary] = sample(state);
5853                for other in primary..K {
5854                    let channel = sample(state);
5855                    tower.h[primary][other] = channel;
5856                    tower.h[other][primary] = channel;
5857                }
5858            }
5859            Order2(tower)
5860        }
5861
5862        fn close(actual: f64, expected: f64, case: usize, label: &str) {
5863            let tolerance = 2.0e-12 * actual.abs().max(expected.abs()).max(1.0);
5864            assert!(
5865                (actual - expected).abs() <= tolerance,
5866                "case {case} {label}: direct={actual:+.16e}, scalar={expected:+.16e}, tolerance={tolerance:.3e}"
5867            );
5868        }
5869
5870        let mut state = 0x932a_ff1e_c0de_5eed_u64;
5871        for case in 0..256 {
5872            // Arbitrary value, gradient, and symmetric-Hessian inputs model the
5873            // complete local state of nonlinear upstream expressions. This is
5874            // stronger than testing only independently seeded variables.
5875            let fixed_inputs: [Order2<K>; TERMS] =
5876                std::array::from_fn(|_| arbitrary_order2(&mut state));
5877            let mut input_scales: [f64; TERMS] = std::array::from_fn(|_| sample(&mut state));
5878            input_scales[0] = -1.25;
5879            input_scales[1] = 0.0;
5880            input_scales[4] = 1.25;
5881            let addend_scales: [f64; TERMS] = std::array::from_fn(|term| match term % 4 {
5882                0 => 0.0,
5883                1 => 1.0,
5884                2 => -0.75,
5885                _ => 0.35,
5886            });
5887            let derivative_stacks: [[f64; 5]; TERMS] =
5888                std::array::from_fn(|_| std::array::from_fn(|_| sample(&mut state)));
5889            let input_shift = sample(&mut state);
5890            let mut fixed_lefts = std::array::from_fn(|term| &fixed_inputs[term]);
5891            fixed_lefts[4] = &fixed_inputs[0];
5892            fixed_lefts[5] = &fixed_inputs[0];
5893            let fixed_right = &fixed_inputs[1];
5894            let fixed_addend = &fixed_inputs[2];
5895
5896            let fixed_product_direct = fixed_inputs[0].product(&fixed_inputs[1]);
5897            let fixed_product_scalar = fixed_inputs[0].mul(&fixed_inputs[1]);
5898            let fixed_affine_direct =
5899                fixed_inputs[2].affine_compose(input_scales[2], input_shift, derivative_stacks[2]);
5900            let fixed_affine_scalar = affine_compose_default(
5901                &fixed_inputs[2],
5902                input_scales[2],
5903                input_shift,
5904                derivative_stacks[2],
5905                JetField::scale,
5906                Order2::add_constant,
5907                JetField::compose_unary,
5908            );
5909            let fixed_sum_direct =
5910                Order2::affine_composed_sum(&fixed_inputs, &input_scales, &derivative_stacks);
5911            let fixed_sum_scalar = affine_composed_sum_default(
5912                &fixed_inputs,
5913                &input_scales,
5914                &derivative_stacks,
5915                Order2::constant,
5916                JetField::add,
5917                JetField::scale,
5918                Order2::add_constant,
5919                JetField::compose_unary,
5920            );
5921            let fixed_fused_direct = Order2::shared_multiply_add_affine_composed_sum(
5922                &fixed_lefts,
5923                fixed_right,
5924                fixed_addend,
5925                &addend_scales,
5926                &input_scales,
5927                &derivative_stacks,
5928            );
5929            let fixed_fused_scalar = shared_multiply_add_affine_composed_sum_default(
5930                &fixed_lefts,
5931                fixed_right,
5932                fixed_addend,
5933                &addend_scales,
5934                &input_scales,
5935                &derivative_stacks,
5936                Order2::constant,
5937                JetField::add,
5938                JetField::mul,
5939                JetField::scale,
5940                Order2::multiply_add,
5941                Order2::affine_compose,
5942            );
5943
5944            let arena = DynamicJetArena::new();
5945            let dynamic_inputs: [DynamicOrder2<'_>; TERMS] = std::array::from_fn(|term| {
5946                DynamicOrder2::from_channel_functions(
5947                    fixed_inputs[term].value(),
5948                    K,
5949                    &arena,
5950                    |primary| fixed_inputs[term].g()[primary],
5951                    |primary, other| fixed_inputs[term].h()[primary][other],
5952                )
5953            });
5954            let dynamic_product_direct = dynamic_inputs[0].product(&dynamic_inputs[1]);
5955            let dynamic_product_scalar = dynamic_inputs[0].mul(&dynamic_inputs[1]);
5956            let dynamic_affine_direct = dynamic_inputs[2].affine_compose(
5957                input_scales[2],
5958                input_shift,
5959                derivative_stacks[2],
5960            );
5961            let dynamic_affine_scalar = affine_compose_default(
5962                &dynamic_inputs[2],
5963                input_scales[2],
5964                input_shift,
5965                derivative_stacks[2],
5966                RuntimeJetScalar::scale,
5967                |input, constant| input.add_constant(constant),
5968                RuntimeJetScalar::compose_unary,
5969            );
5970            let dynamic_sum_direct = DynamicOrder2::affine_composed_sum(
5971                &dynamic_inputs,
5972                &input_scales,
5973                &derivative_stacks,
5974                K,
5975                &arena,
5976            );
5977            let dynamic_sum_scalar = affine_composed_sum_default(
5978                &dynamic_inputs,
5979                &input_scales,
5980                &derivative_stacks,
5981                |value| DynamicOrder2::constant(value, K, &arena),
5982                RuntimeJetScalar::add,
5983                RuntimeJetScalar::scale,
5984                |input, constant| input.add_constant(constant),
5985                RuntimeJetScalar::compose_unary,
5986            );
5987            let mut dynamic_lefts = std::array::from_fn(|term| &dynamic_inputs[term]);
5988            dynamic_lefts[4] = &dynamic_inputs[0];
5989            dynamic_lefts[5] = &dynamic_inputs[0];
5990            let dynamic_right = &dynamic_inputs[1];
5991            let dynamic_addend = &dynamic_inputs[2];
5992            let dynamic_fused_direct = DynamicOrder2::shared_multiply_add_affine_composed_sum(
5993                &dynamic_lefts,
5994                dynamic_right,
5995                dynamic_addend,
5996                &addend_scales,
5997                &input_scales,
5998                &derivative_stacks,
5999                K,
6000                &arena,
6001            );
6002            let dynamic_fused_scalar = shared_multiply_add_affine_composed_sum_default(
6003                &dynamic_lefts,
6004                dynamic_right,
6005                dynamic_addend,
6006                &addend_scales,
6007                &input_scales,
6008                &derivative_stacks,
6009                |value| DynamicOrder2::constant(value, K, &arena),
6010                RuntimeJetScalar::add,
6011                RuntimeJetScalar::mul,
6012                RuntimeJetScalar::scale,
6013                RuntimeJetScalar::multiply_add,
6014                |input, scale, shift, stack| input.affine_compose(scale, shift, stack),
6015            );
6016
6017            for (label, actual, expected) in [
6018                (
6019                    "fixed product value",
6020                    fixed_product_direct.value(),
6021                    fixed_product_scalar.value(),
6022                ),
6023                (
6024                    "fixed affine value",
6025                    fixed_affine_direct.value(),
6026                    fixed_affine_scalar.value(),
6027                ),
6028                (
6029                    "fixed affine sum value",
6030                    fixed_sum_direct.value(),
6031                    fixed_sum_scalar.value(),
6032                ),
6033                (
6034                    "fixed fused value",
6035                    fixed_fused_direct.value(),
6036                    fixed_fused_scalar.value(),
6037                ),
6038                (
6039                    "dynamic product value",
6040                    dynamic_product_direct.value(),
6041                    dynamic_product_scalar.value(),
6042                ),
6043                (
6044                    "dynamic affine value",
6045                    dynamic_affine_direct.value(),
6046                    dynamic_affine_scalar.value(),
6047                ),
6048                (
6049                    "dynamic affine sum value",
6050                    dynamic_sum_direct.value(),
6051                    dynamic_sum_scalar.value(),
6052                ),
6053                (
6054                    "dynamic fused value",
6055                    dynamic_fused_direct.value(),
6056                    dynamic_fused_scalar.value(),
6057                ),
6058            ] {
6059                close(actual, expected, case, label);
6060            }
6061            for primary in 0..K {
6062                for (label, actual, expected) in [
6063                    (
6064                        "fixed product gradient",
6065                        fixed_product_direct.g()[primary],
6066                        fixed_product_scalar.g()[primary],
6067                    ),
6068                    (
6069                        "fixed affine gradient",
6070                        fixed_affine_direct.g()[primary],
6071                        fixed_affine_scalar.g()[primary],
6072                    ),
6073                    (
6074                        "fixed affine sum gradient",
6075                        fixed_sum_direct.g()[primary],
6076                        fixed_sum_scalar.g()[primary],
6077                    ),
6078                    (
6079                        "fixed fused gradient",
6080                        fixed_fused_direct.g()[primary],
6081                        fixed_fused_scalar.g()[primary],
6082                    ),
6083                    (
6084                        "dynamic product gradient",
6085                        dynamic_product_direct.g()[primary],
6086                        dynamic_product_scalar.g()[primary],
6087                    ),
6088                    (
6089                        "dynamic affine gradient",
6090                        dynamic_affine_direct.g()[primary],
6091                        dynamic_affine_scalar.g()[primary],
6092                    ),
6093                    (
6094                        "dynamic affine sum gradient",
6095                        dynamic_sum_direct.g()[primary],
6096                        dynamic_sum_scalar.g()[primary],
6097                    ),
6098                    (
6099                        "dynamic fused gradient",
6100                        dynamic_fused_direct.g()[primary],
6101                        dynamic_fused_scalar.g()[primary],
6102                    ),
6103                ] {
6104                    close(actual, expected, case, label);
6105                }
6106                for other in 0..K {
6107                    for (label, actual, expected) in [
6108                        (
6109                            "fixed product Hessian",
6110                            fixed_product_direct.h()[primary][other],
6111                            fixed_product_scalar.h()[primary][other],
6112                        ),
6113                        (
6114                            "fixed affine Hessian",
6115                            fixed_affine_direct.h()[primary][other],
6116                            fixed_affine_scalar.h()[primary][other],
6117                        ),
6118                        (
6119                            "fixed affine sum Hessian",
6120                            fixed_sum_direct.h()[primary][other],
6121                            fixed_sum_scalar.h()[primary][other],
6122                        ),
6123                        (
6124                            "fixed fused Hessian",
6125                            fixed_fused_direct.h()[primary][other],
6126                            fixed_fused_scalar.h()[primary][other],
6127                        ),
6128                        (
6129                            "dynamic product Hessian",
6130                            dynamic_product_direct.h_at(primary, other),
6131                            dynamic_product_scalar.h_at(primary, other),
6132                        ),
6133                        (
6134                            "dynamic affine Hessian",
6135                            dynamic_affine_direct.h_at(primary, other),
6136                            dynamic_affine_scalar.h_at(primary, other),
6137                        ),
6138                        (
6139                            "dynamic affine sum Hessian",
6140                            dynamic_sum_direct.h_at(primary, other),
6141                            dynamic_sum_scalar.h_at(primary, other),
6142                        ),
6143                        (
6144                            "dynamic fused Hessian",
6145                            dynamic_fused_direct.h_at(primary, other),
6146                            dynamic_fused_scalar.h_at(primary, other),
6147                        ),
6148                    ] {
6149                        close(actual, expected, case, label);
6150                    }
6151                }
6152            }
6153        }
6154    }
6155
6156    #[test]
6157    fn shared_product_composition_accepts_empty_expression() {
6158        const K: usize = 4;
6159        let fixed_terms: [&Order2<K>; 0] = [];
6160        let fixed_shared = Order2::constant(1.0);
6161        let scales: [f64; 0] = [];
6162        let stacks: [[f64; 5]; 0] = [];
6163        let fixed = Order2::shared_multiply_add_affine_composed_sum(
6164            &fixed_terms,
6165            &fixed_shared,
6166            &fixed_shared,
6167            &scales,
6168            &scales,
6169            &stacks,
6170        );
6171        assert_eq!(fixed.value().to_bits(), 0.0_f64.to_bits());
6172        assert!(fixed.g().iter().all(|&channel| channel == 0.0));
6173        assert!(fixed.h().iter().flatten().all(|&channel| channel == 0.0));
6174
6175        let value_terms: [&RuntimeValue; 0] = [];
6176        let value_shared = RuntimeValue::constant(1.0, K, &());
6177        let value = RuntimeValue::shared_multiply_add_affine_composed_sum(
6178            &value_terms,
6179            &value_shared,
6180            &value_shared,
6181            &scales,
6182            &scales,
6183            &stacks,
6184            K,
6185            &(),
6186        );
6187        assert_eq!(value.value().to_bits(), 0.0_f64.to_bits());
6188        assert_eq!(value.dimension(), K);
6189
6190        let arena = DynamicJetArena::new();
6191        let dynamic_terms: [&DynamicOrder2<'_>; 0] = [];
6192        let dynamic_shared = DynamicOrder2::constant(1.0, K, &arena);
6193        let dynamic = DynamicOrder2::shared_multiply_add_affine_composed_sum(
6194            &dynamic_terms,
6195            &dynamic_shared,
6196            &dynamic_shared,
6197            &scales,
6198            &scales,
6199            &stacks,
6200            K,
6201            &arena,
6202        );
6203        assert_eq!(dynamic.value().to_bits(), 0.0_f64.to_bits());
6204        assert!((0..K).all(|axis| dynamic.g()[axis] == 0.0));
6205        assert!((0..K).all(|row| (0..K).all(|column| dynamic.h_at(row, column) == 0.0)));
6206    }
6207
6208    #[test]
6209    fn runtime_fused_product_composition_preserves_tower4_channels() {
6210        const K: usize = 2;
6211        const N: usize = 9;
6212        let vars = [
6213            Tower4::<K>::variable(0.37, 0),
6214            Tower4::<K>::variable(-0.61, 1),
6215        ];
6216        let upstream = [
6217            vars[0].mul(&vars[1]).add(&vars[0].exp()),
6218            vars[1].mul(&vars[1]).add(&vars[0].scale(0.3)),
6219            vars[0].mul(&vars[0]).sub(&vars[1].scale(-0.2)),
6220        ];
6221        let mut lefts: [Tower4<K>; N] = std::array::from_fn(|term| upstream[term % upstream.len()]);
6222        lefts[4] = lefts[0];
6223        lefts[5] = lefts[0];
6224        let right = upstream[1];
6225        let addend = upstream[2];
6226        let addend_scales: [f64; N] = std::array::from_fn(|term| [-0.0, 1.0, -0.7, 0.25][term % 4]);
6227        let mut input_scales: [f64; N] =
6228            std::array::from_fn(|term| [0.0, -1.3, 0.45, 1.1][term % 4]);
6229        input_scales[0] = -1.1;
6230        input_scales[4] = 1.1;
6231        let stacks: [[f64; 5]; N] = std::array::from_fn(|term| {
6232            let t = term as f64 + 1.0;
6233            [0.17 * t, -0.11 * t, 0.07 * t, -0.03 * t, 0.013 * t]
6234        });
6235
6236        let expected = (0..N).fold(Tower4::<K>::constant(0.0), |sum, term| {
6237            let inner = if addend_scales[term] == 0.0 {
6238                lefts[term].mul(&right)
6239            } else if addend_scales[term] == 1.0 {
6240                JetScalar::multiply_add(&lefts[term], &right, &addend)
6241            } else {
6242                JetScalar::multiply_add(&lefts[term], &right, &addend.scale(addend_scales[term]))
6243            };
6244            sum.add(&JetScalar::affine_compose(
6245                &inner,
6246                input_scales[term],
6247                0.0,
6248                stacks[term],
6249            ))
6250        });
6251        let wrapped_lefts: [FixedRuntimeJet<Tower4<K>, K>; N] =
6252            std::array::from_fn(|term| FixedRuntimeJet::from_inner(lefts[term]));
6253        let wrapped_right = FixedRuntimeJet::from_inner(right);
6254        let wrapped_addend = FixedRuntimeJet::from_inner(addend);
6255        let mut wrapped_left_refs: [&FixedRuntimeJet<Tower4<K>, K>; N] =
6256            std::array::from_fn(|term| &wrapped_lefts[term]);
6257        wrapped_left_refs[4] = &wrapped_lefts[0];
6258        wrapped_left_refs[5] = &wrapped_lefts[0];
6259        let actual = FixedRuntimeJet::<Tower4<K>, K>::shared_multiply_add_affine_composed_sum(
6260            &wrapped_left_refs,
6261            &wrapped_right,
6262            &wrapped_addend,
6263            &addend_scales,
6264            &input_scales,
6265            &stacks,
6266            K,
6267            &(),
6268        )
6269        .into_inner();
6270
6271        let same = |label: &str, got: f64, want: f64| {
6272            let tolerance = 2.0e-13 * got.abs().max(want.abs()).max(1.0);
6273            assert!(
6274                (got - want).abs() <= tolerance,
6275                "{label}: got={got:+.17e}, want={want:+.17e}, tolerance={tolerance:.3e}"
6276            );
6277        };
6278        same("value", actual.v, expected.v);
6279        for a in 0..K {
6280            same("gradient", actual.g[a], expected.g[a]);
6281            for b in 0..K {
6282                same("Hessian", actual.h[a][b], expected.h[a][b]);
6283                for c in 0..K {
6284                    same("third", actual.t3[a][b][c], expected.t3[a][b][c]);
6285                    for d in 0..K {
6286                        same("fourth", actual.t4[a][b][c][d], expected.t4[a][b][c][d]);
6287                    }
6288                }
6289            }
6290        }
6291    }
6292
6293    /// A small polynomial-plus-unary row expression written ONCE, generically
6294    /// over `S: JetScalar<2>`, so it can be evaluated against every scalar:
6295    /// `ℓ = (e^{p0·p1} + 2) · √(p0·p0 + 1) − p1·p1·0.5`.
6296    /// Exercises mul, add/sub, scale, exp, sqrt — every algebra op.
6297    fn row_expr<S: JetScalar<2>>(p: &[S; 2]) -> S {
6298        let g = p[0].mul(&p[1]).exp();
6299        let inner = g.add(&S::constant(2.0));
6300        let radic = p[0].mul(&p[0]).add(&S::constant(1.0)).sqrt();
6301        inner.mul(&radic).sub(&p[1].mul(&p[1]).scale(0.5))
6302    }
6303
6304    /// The same expression exposed through the canonical generic row-program seam.
6305    struct ExprProgram {
6306        p: [f64; 2],
6307    }
6308    impl RowProgram<2> for ExprProgram {
6309        fn n_rows(&self) -> usize {
6310            1
6311        }
6312        fn primaries(&self, row: usize) -> Result<[f64; 2], String> {
6313            if row >= self.n_rows() {
6314                return Err(format!("ExprProgram: row {row} out of range"));
6315            }
6316            Ok(self.p)
6317        }
6318        fn eval<S: JetScalar<2>>(&self, row: usize, p: &[S; 2]) -> Result<S, String> {
6319            if row >= self.n_rows() {
6320                return Err(format!("ExprProgram: row {row} out of range"));
6321            }
6322            Ok(row_expr(p))
6323        }
6324    }
6325
6326    const SEED: [f64; 2] = [0.37, -0.81];
6327    const U: [f64; 2] = [0.6, -0.2];
6328    const V: [f64; 2] = [-0.4, 1.1];
6329    const TOL: f64 = 1e-10;
6330
6331    fn close(a: f64, b: f64, label: &str) {
6332        let band = TOL + TOL * a.abs().max(b.abs());
6333        assert!(
6334            (a - b).abs() <= band,
6335            "{label}: {a:+.15e} vs {b:+.15e} (band {band:.3e})"
6336        );
6337    }
6338
6339    fn tower() -> Tower4<2> {
6340        *program_full_tower(&ExprProgram { p: SEED }, 0).expect("tower")
6341    }
6342
6343    /// Order2 reproduces Tower4's value/grad/Hessian channels exactly.
6344    #[test]
6345    fn order2_matches_tower_value_grad_hessian() {
6346        let t = tower();
6347        let vars: [Order2<2>; 2] = std::array::from_fn(|a| Order2::variable(SEED[a], a));
6348        let s = row_expr(&vars);
6349        close(s.value(), t.v, "value");
6350        for a in 0..2 {
6351            close(s.0.g[a], t.g[a], &format!("grad[{a}]"));
6352            for b in 0..2 {
6353                close(s.h()[a][b], t.h[a][b], &format!("hess[{a}][{b}]"));
6354            }
6355        }
6356    }
6357
6358    #[test]
6359    fn mapped_order2_accumulator_matches_dense_overlapping_atoms() {
6360        const K: usize = 4;
6361        let p = [0.2_f64, 0.7, -0.4, 0.3];
6362        let dense_vars: [Order2<K>; K] =
6363            std::array::from_fn(|axis| Order2::variable(p[axis], axis));
6364        let dense_q0 = dense_vars[3].mul(&dense_vars[1]).add(&dense_vars[3].exp());
6365        let dense_q1 = dense_vars[1].mul(&dense_vars[2]).sub(&dense_vars[2]);
6366        let dense = dense_q0.ln().add(&dense_q1.exp());
6367
6368        let local_q0_vars: [Order2<2>; 2] =
6369            std::array::from_fn(|axis| Order2::variable(p[[3, 1][axis]], axis));
6370        let local_q0 = local_q0_vars[0]
6371            .mul(&local_q0_vars[1])
6372            .add(&local_q0_vars[0].exp());
6373        let local_q1_vars: [Order2<2>; 2] =
6374            std::array::from_fn(|axis| Order2::variable(p[[1, 2][axis]], axis));
6375        let local_q1 = local_q1_vars[0]
6376            .mul(&local_q1_vars[1])
6377            .sub(&local_q1_vars[1]);
6378
6379        let q0 = local_q0.value();
6380        let q1_exp = local_q1.value().exp();
6381        let mut lowered = MappedOrder2Accumulator::<K>::zero();
6382        lowered.add_composed(
6383            &local_q0,
6384            [3, 1],
6385            [q0.ln(), q0.recip(), -1.0 / (q0 * q0)],
6386            false,
6387            [false, false],
6388            [false, false, false],
6389        );
6390        lowered.add_composed(
6391            &local_q1,
6392            [1, 2],
6393            [q1_exp, q1_exp, q1_exp],
6394            true,
6395            [true, false],
6396            [true, false, false],
6397        );
6398        let (value, gradient, hessian) = lowered.into_channels();
6399
6400        close(value, dense.value(), "mapped value");
6401        for i in 0..K {
6402            close(gradient[i], dense.g()[i], &format!("mapped gradient[{i}]"));
6403            for j in 0..K {
6404                close(
6405                    hessian[i][j],
6406                    dense.h()[i][j],
6407                    &format!("mapped Hessian[{i},{j}]"),
6408                );
6409            }
6410        }
6411    }
6412
6413    #[test]
6414    #[should_panic(expected = "mapped atom axes must be injective")]
6415    fn mapped_order2_accumulator_rejects_duplicate_axes() {
6416        let vars: [Order2<2>; 2] = std::array::from_fn(|axis| Order2::variable(0.2, axis));
6417        let atom = vars[0].add(&vars[1]);
6418        let mut lowered = MappedOrder2Accumulator::<2>::zero();
6419        lowered.add_composed(
6420            &atom,
6421            [1, 1],
6422            [0.4, 1.0, 0.0],
6423            false,
6424            [false, false],
6425            [false, false, false],
6426        );
6427    }
6428
6429    #[test]
6430    #[should_panic(expected = "mapped atom axis must be within")]
6431    fn mapped_order2_accumulator_rejects_out_of_range_axes() {
6432        let atom = Order2::<1>::variable(0.2, 0);
6433        let mut lowered = MappedOrder2Accumulator::<2>::zero();
6434        lowered.add_composed(&atom, [2], [0.2, 1.0, 0.0], false, [false], [false]);
6435    }
6436
6437    #[test]
6438    fn dynamic_order2_accumulator_matches_dense_composed_sum() {
6439        const K: usize = 4;
6440
6441        struct Term {
6442            first: f64,
6443            second: f64,
6444            gradient: [f64; K],
6445            hessian: [[f64; K]; K],
6446        }
6447
6448        impl DynamicOrder2Term for Term {
6449            fn outer_first(&self) -> f64 {
6450                self.first
6451            }
6452
6453            fn outer_second(&self) -> f64 {
6454                self.second
6455            }
6456
6457            fn inner_gradient(&self, axis: usize) -> f64 {
6458                self.gradient[axis]
6459            }
6460
6461            fn inner_hessian(&self, row: usize, column: usize) -> f64 {
6462                self.hessian[row][column]
6463            }
6464        }
6465
6466        let p = [0.7, -0.3, 0.2, 0.8];
6467        let vars: [Order2<K>; K] = std::array::from_fn(|axis| Order2::variable(p[axis], axis));
6468        let first_atom = vars[0]
6469            .mul(&vars[1])
6470            .add(&vars[2].exp())
6471            .add(&Order2::constant(1.5));
6472        let second_atom = vars[1].mul(&vars[3]).sub(&vars[0]);
6473        let first_value = first_atom.value();
6474        let second_exp = second_atom.value().exp();
6475        let first_stack = [
6476            first_value.ln(),
6477            first_value.recip(),
6478            -1.0 / (first_value * first_value),
6479            0.0,
6480            0.0,
6481        ];
6482        let second_stack = [second_exp, second_exp, second_exp, second_exp, second_exp];
6483        let dense = first_atom
6484            .compose_unary(first_stack)
6485            .add(&second_atom.compose_unary(second_stack));
6486        let terms = [
6487            Term {
6488                first: first_stack[1],
6489                second: first_stack[2],
6490                gradient: *first_atom.g(),
6491                hessian: *first_atom.h(),
6492            },
6493            Term {
6494                first: second_stack[1],
6495                second: second_stack[2],
6496                gradient: *second_atom.g(),
6497                hessian: *second_atom.h(),
6498            },
6499        ];
6500        let (value, gradient, hessian) = DynamicOrder2Accumulator::from_composed_sum(
6501            K,
6502            first_stack[0] + second_stack[0],
6503            &terms,
6504        )
6505        .into_channels();
6506
6507        close(value, dense.value(), "dynamic value");
6508        for row in 0..K {
6509            close(
6510                gradient[row],
6511                dense.g()[row],
6512                &format!("dynamic gradient[{row}]"),
6513            );
6514            for column in 0..K {
6515                close(
6516                    hessian[row * K + column],
6517                    dense.h()[row][column],
6518                    &format!("dynamic Hessian[{row},{column}]"),
6519                );
6520            }
6521        }
6522    }
6523
6524    #[derive(Clone, Copy, Debug)]
6525    struct FullTwoPattern;
6526
6527    impl HessianPattern<2, 3> for FullTwoPattern {
6528        const PAIRS: [(usize, usize); 3] = [(0, 0), (0, 1), (1, 1)];
6529        const PAIR_BITS: [[u128; 2]; 2] = hessian_pair_bits(Self::PAIRS);
6530    }
6531
6532    /// Patterned order-two arithmetic is channel-identical to dense `Order2`
6533    /// when the pattern covers the expression's complete Hessian support.
6534    #[test]
6535    fn patterned_order2_matches_dense_order2() {
6536        type Sparse = PatternedOrder2<FullTwoPattern, 2, 3>;
6537        let dense_vars: [Order2<2>; 2] = std::array::from_fn(|a| Order2::variable(SEED[a], a));
6538        let sparse_vars: [Sparse; 2] = std::array::from_fn(|a| Sparse::variable(SEED[a], a));
6539        let dense = row_expr(&dense_vars);
6540        let sparse = row_expr(&sparse_vars);
6541        close(sparse.value(), dense.value(), "patterned value");
6542        for i in 0..2 {
6543            close(sparse.g()[i], dense.g()[i], &format!("patterned grad[{i}]"));
6544            for j in 0..2 {
6545                close(
6546                    sparse.h()[i][j],
6547                    dense.h()[i][j],
6548                    &format!("patterned hess[{i}][{j}]"),
6549                );
6550            }
6551        }
6552    }
6553
6554    /// `compose_unary_with` is `to_bits`-identical to the explicit
6555    /// `compose_unary(stack_fn(value))` form. Exercised on [`Order2`] across
6556    /// `K ∈ {2,3,4,9}`, ≥ 4000 random seeded inputs.
6557    #[test]
6558    fn compose_unary_with_scalar_seam_bit_identical() {
6559        fn rand_unit(state: &mut u64) -> f64 {
6560            let mut x = *state;
6561            x ^= x << 13;
6562            x ^= x >> 7;
6563            x ^= x << 17;
6564            *state = x;
6565            2.0 * ((x >> 11) as f64 / ((1u64 << 53) as f64)) - 1.0
6566        }
6567        // A base-value-dependent finite stack standing in for a family stack.
6568        fn stack(u: f64) -> [f64; 5] {
6569            [
6570                u.sin(),
6571                u.cos(),
6572                (2.0 * u).sin(),
6573                (0.5 * u).cos(),
6574                u * u - 0.3,
6575            ]
6576        }
6577        fn run<const K: usize>(state: &mut u64, n: usize) -> usize {
6578            for _ in 0..n {
6579                // A non-trivial Order2<K> jet: a seeded variable pushed through a
6580                // couple of algebra ops so g/h are dense, then exercise the seam.
6581                let base = rand_unit(state);
6582                let mut s = Order2::<K>::variable(base, 0);
6583                for a in 1..K {
6584                    s = crate::nested_dual::JetField::mul(
6585                        &s,
6586                        &Order2::<K>::variable(rand_unit(state), a),
6587                    );
6588                }
6589                let with = s.compose_unary_with(stack);
6590                let explicit = s.compose_unary(stack(s.value()));
6591                assert_eq!(with.value().to_bits(), explicit.value().to_bits(), "value");
6592                for a in 0..K {
6593                    assert_eq!(with.g()[a].to_bits(), explicit.g()[a].to_bits(), "g[{a}]");
6594                    for b in 0..K {
6595                        assert_eq!(
6596                            with.h()[a][b].to_bits(),
6597                            explicit.h()[a][b].to_bits(),
6598                            "h[{a}][{b}]"
6599                        );
6600                    }
6601                }
6602            }
6603            n
6604        }
6605        let mut st = 0x9e37_79b9_7f4a_7c15u64;
6606        let total = run::<2>(&mut st, 1100)
6607            + run::<3>(&mut st, 1100)
6608            + run::<4>(&mut st, 1100)
6609            + run::<9>(&mut st, 1100);
6610        assert_eq!(total, 4400);
6611    }
6612
6613    /// OneSeed's ε-Hessian is the contracted third Σ_c ℓ_{abc} u_c, matching
6614    /// `Tower4::third_contracted(u)`. Base channels also match the tower.
6615    #[test]
6616    fn one_seed_matches_tower_third_contracted() {
6617        let t = tower();
6618        let truth = t.third_contracted(&U);
6619        let vars: [OneSeed<2>; 2] =
6620            std::array::from_fn(|a| OneSeed::seed_direction(SEED[a], a, U[a]));
6621        let s = row_expr(&vars);
6622        // Base channels are the plain (v, g, H).
6623        close(s.value(), t.v, "value");
6624        for a in 0..2 {
6625            for b in 0..2 {
6626                close(s.base.h()[a][b], t.h[a][b], &format!("base hess[{a}][{b}]"));
6627            }
6628        }
6629        let third = s.contracted_third();
6630        for a in 0..2 {
6631            for b in 0..2 {
6632                close(third[a][b], truth[a][b], &format!("third[{a}][{b}]"));
6633            }
6634        }
6635    }
6636
6637    /// The fused fixed-width OneSeed product/composition preserves every
6638    /// channel of the defining unfused nilpotent algebra. Random symmetric
6639    /// Order2 inputs exercise dense non-seed intermediates, while exact
6640    /// mirrored symmetry is checked as an independent production invariant.
6641    #[test]
6642    fn fused_one_seed_channels_match_unfused_definition_932() {
6643        const K: usize = 8;
6644
6645        fn random_scalar(state: &mut u64) -> f64 {
6646            *state ^= *state << 13;
6647            *state ^= *state >> 7;
6648            *state ^= *state << 17;
6649            ((*state >> 11) as f64 / ((1_u64 << 53) as f64)) * 2.0 - 1.0
6650        }
6651
6652        fn random_order2<const N: usize>(state: &mut u64) -> Order2<N> {
6653            let mut tower = crate::jet_tower::Tower2::<N>::zero();
6654            tower.v = random_scalar(state);
6655            for axis in 0..N {
6656                tower.g[axis] = random_scalar(state);
6657            }
6658            for row in 0..N {
6659                for column in row..N {
6660                    let channel = random_scalar(state);
6661                    tower.h[row][column] = channel;
6662                    tower.h[column][row] = channel;
6663                }
6664            }
6665            Order2(tower)
6666        }
6667
6668        fn assert_channels_close<const N: usize>(
6669            label: &str,
6670            actual: &OneSeed<N>,
6671            expected: &OneSeed<N>,
6672        ) {
6673            for (part_label, actual_part, expected_part, require_exact_symmetry) in [
6674                ("base", &actual.base.0, &expected.base.0, false),
6675                ("eps", &actual.eps.0, &expected.eps.0, true),
6676            ] {
6677                let check = |channel: &str, got: f64, want: f64| {
6678                    let tolerance = 2.0e-14 * got.abs().max(want.abs()).max(1.0);
6679                    assert!(
6680                        (got - want).abs() <= tolerance,
6681                        "{label} {part_label} {channel}: got={got:+.17e} want={want:+.17e}"
6682                    );
6683                };
6684                check("value", actual_part.v, expected_part.v);
6685                for row in 0..N {
6686                    check(
6687                        &format!("gradient[{row}]"),
6688                        actual_part.g[row],
6689                        expected_part.g[row],
6690                    );
6691                    for column in 0..N {
6692                        check(
6693                            &format!("hessian[{row},{column}]"),
6694                            actual_part.h[row][column],
6695                            expected_part.h[row][column],
6696                        );
6697                        if require_exact_symmetry {
6698                            assert_eq!(
6699                                actual_part.h[row][column].to_bits(),
6700                                actual_part.h[column][row].to_bits(),
6701                                "{label} {part_label} Hessian symmetry at [{row},{column}]"
6702                            );
6703                        }
6704                    }
6705                }
6706            }
6707        }
6708
6709        let mut state = 0x9320_1eed_5eed_cafe_u64;
6710        for sample in 0..256 {
6711            let left = OneSeed {
6712                base: random_order2::<K>(&mut state),
6713                eps: random_order2::<K>(&mut state),
6714            };
6715            let right = OneSeed {
6716                base: random_order2::<K>(&mut state),
6717                eps: random_order2::<K>(&mut state),
6718            };
6719
6720            let fused_product = left.mul(&right);
6721            let unfused_product = OneSeed {
6722                base: left.base.mul(&right.base),
6723                eps: left.base.mul(&right.eps).add(&left.eps.mul(&right.base)),
6724            };
6725            assert_channels_close(
6726                &format!("sample {sample} product"),
6727                &fused_product,
6728                &unfused_product,
6729            );
6730
6731            let derivatives: [f64; 5] = std::array::from_fn(|_| random_scalar(&mut state));
6732            let fused_composition = left.compose_unary(derivatives);
6733            let unfused_composition = OneSeed {
6734                base: left.base.compose_unary(derivatives),
6735                eps: left
6736                    .base
6737                    .compose_unary([
6738                        derivatives[1],
6739                        derivatives[2],
6740                        derivatives[3],
6741                        derivatives[4],
6742                        derivatives[4],
6743                    ])
6744                    .mul(&left.eps),
6745            };
6746            assert_channels_close(
6747                &format!("sample {sample} composition"),
6748                &fused_composition,
6749                &unfused_composition,
6750            );
6751        }
6752    }
6753
6754    /// TwoSeed's εδ-Hessian is the contracted fourth Σ_{cd} ℓ_{abcd} u_c v_d,
6755    /// matching `Tower4::fourth_contracted(u, v)`. The ε / δ single-seed parts
6756    /// reproduce the two third contractions Σ_c ℓ_{abc} u_c and …v_d.
6757    #[test]
6758    fn two_seed_matches_tower_fourth_contracted() {
6759        let t = tower();
6760        let truth4 = t.fourth_contracted(&U, &V);
6761        let truth3_u = t.third_contracted(&U);
6762        let truth3_v = t.third_contracted(&V);
6763        let vars: [TwoSeed<2>; 2] = std::array::from_fn(|a| TwoSeed::seed(SEED[a], a, U[a], V[a]));
6764        let s = row_expr(&vars);
6765        close(s.value(), t.v, "value");
6766        for a in 0..2 {
6767            close(s.base.0.g[a], t.g[a], &format!("grad[{a}]"));
6768            for b in 0..2 {
6769                close(s.base.h()[a][b], t.h[a][b], &format!("base hess[{a}][{b}]"));
6770                close(
6771                    s.eps.h()[a][b],
6772                    truth3_u[a][b],
6773                    &format!("eps third_u[{a}][{b}]"),
6774                );
6775                close(
6776                    s.del.h()[a][b],
6777                    truth3_v[a][b],
6778                    &format!("del third_v[{a}][{b}]"),
6779                );
6780            }
6781        }
6782        let fourth = s.contracted_fourth();
6783        for a in 0..2 {
6784            for b in 0..2 {
6785                close(fourth[a][b], truth4[a][b], &format!("fourth[{a}][{b}]"));
6786            }
6787        }
6788    }
6789
6790    /// The generic `row_nll_generic` seam (added to Tower4's program trait
6791    /// surface) evaluates the SAME expression on each scalar and extracts the
6792    /// channel a consumer asks for, agreeing with the direct Tower4 contraction.
6793    #[test]
6794    fn generic_program_seam_matches_tower_for_every_channel() {
6795        let t = tower();
6796        // Order2 via generic seam.
6797        let o2: [Order2<2>; 2] = std::array::from_fn(|a| Order2::variable(SEED[a], a));
6798        let so2 = row_expr(&o2);
6799        close(so2.value(), t.v, "seam order2 value");
6800        // OneSeed third.
6801        let os: [OneSeed<2>; 2] =
6802            std::array::from_fn(|a| OneSeed::seed_direction(SEED[a], a, U[a]));
6803        let third = row_expr(&os).contracted_third();
6804        let truth3 = t.third_contracted(&U);
6805        for a in 0..2 {
6806            for b in 0..2 {
6807                close(third[a][b], truth3[a][b], &format!("seam third[{a}][{b}]"));
6808            }
6809        }
6810        // TwoSeed fourth.
6811        let ts: [TwoSeed<2>; 2] = std::array::from_fn(|a| TwoSeed::seed(SEED[a], a, U[a], V[a]));
6812        let fourth = row_expr(&ts).contracted_fourth();
6813        let truth4 = t.fourth_contracted(&U, &V);
6814        for a in 0..2 {
6815            for b in 0..2 {
6816                close(
6817                    fourth[a][b],
6818                    truth4[a][b],
6819                    &format!("seam fourth[{a}][{b}]"),
6820                );
6821            }
6822        }
6823    }
6824
6825    /// The (test-only) `Tower4: JetScalar` impl is the all-channels oracle scalar:
6826    /// evaluating the SAME generic `row_expr` at `S = Tower4` (through the
6827    /// `JetScalar` trait ops) must reproduce, channel-for-channel, the `Tower4`
6828    /// obtained through the canonical `RowProgram` path (`program_full_tower`).
6829    /// This pins that the full-tower scalar and the contracted scalar oracles above
6830    /// all execute the same generic algebra surface.
6831    #[test]
6832    fn tower4_as_jetscalar_matches_program_tower_all_channels() {
6833        let t = tower();
6834        let vars: [Tower4<2>; 2] = std::array::from_fn(|a| Tower4::variable(SEED[a], a));
6835        let s = row_expr(&vars);
6836        close(s.v, t.v, "tower-jetscalar value");
6837        for a in 0..2 {
6838            close(s.g[a], t.g[a], &format!("tower-jetscalar grad[{a}]"));
6839            for b in 0..2 {
6840                close(
6841                    s.h[a][b],
6842                    t.h[a][b],
6843                    &format!("tower-jetscalar hess[{a}][{b}]"),
6844                );
6845                for c in 0..2 {
6846                    close(
6847                        s.t3[a][b][c],
6848                        t.t3[a][b][c],
6849                        &format!("tower-jetscalar t3[{a}][{b}][{c}]"),
6850                    );
6851                    for d in 0..2 {
6852                        close(
6853                            s.t4[a][b][c][d],
6854                            t.t4[a][b][c][d],
6855                            &format!("tower-jetscalar t4[{a}][{b}][{c}][{d}]"),
6856                        );
6857                    }
6858                }
6859            }
6860        }
6861    }
6862
6863    /// Arena-backed runtime directional jets must reproduce the fixed packed
6864    /// algebras channel-for-channel. This isolates the runtime scalar algebra
6865    /// from every family row program before the SLS/SAE integration oracles.
6866    #[test]
6867    fn runtime_directional_jets_match_fixed_packed_algebra_932() {
6868        fn expression<'arena, S: RuntimeJetScalar<'arena>>(vars: &[S]) -> S {
6869            let bilinear = vars[0].mul(&vars[1]);
6870            let curved = vars[2].scale(0.7).add(&vars[3].mul(&vars[3]).scale(-0.2));
6871            bilinear
6872                .add(&curved)
6873                .exp()
6874                .mul(&vars[4].compose_unary([0.4, -0.3, 0.2, -0.1, 0.05]))
6875        }
6876
6877        const K: usize = 5;
6878        let values = [0.2, -0.7, 0.4, 1.1, -0.3];
6879        let direction_u = [0.5, -0.2, 0.7, -0.4, 0.1];
6880        let direction_v = [-0.3, 0.8, 0.2, 0.6, -0.5];
6881        let close = |actual: f64, expected: f64| {
6882            let tolerance = 1.0e-13 * (1.0 + actual.abs().max(expected.abs()));
6883            assert!((actual - expected).abs() <= tolerance);
6884        };
6885
6886        let fixed_one: Vec<FixedRuntimeJet<OneSeed<K>, K>> = (0..K)
6887            .map(|axis| FixedRuntimeJet {
6888                inner: OneSeed::seed_direction(values[axis], axis, direction_u[axis]),
6889            })
6890            .collect();
6891        let arena_one = DynamicJetArena::new();
6892        let dynamic_one: Vec<DynamicOneSeed<'_>> = (0..K)
6893            .map(|axis| {
6894                DynamicOneSeed::seed_direction(values[axis], axis, direction_u[axis], K, &arena_one)
6895            })
6896            .collect();
6897        let fixed_third = expression(&fixed_one).into_inner().contracted_third();
6898        let dynamic_third = expression(&dynamic_one);
6899        for a in 0..K {
6900            for b in 0..K {
6901                assert_eq!(
6902                    dynamic_third.contracted_third()[a * K + b].to_bits(),
6903                    dynamic_third.contracted_third()[b * K + a].to_bits(),
6904                    "arena third Hessian must be exactly symmetric at ({a},{b})"
6905                );
6906                close(
6907                    dynamic_third.contracted_third()[a * K + b],
6908                    fixed_third[a][b],
6909                );
6910            }
6911        }
6912
6913        let fixed_one_v: Vec<FixedRuntimeJet<OneSeed<K>, K>> = (0..K)
6914            .map(|axis| FixedRuntimeJet {
6915                inner: OneSeed::seed_direction(values[axis], axis, direction_v[axis]),
6916            })
6917            .collect();
6918        let fixed_third_v = expression(&fixed_one_v).into_inner().contracted_third();
6919        let batch_workspace = DynamicJetBatchWorkspace::new(2);
6920        let directions = [direction_u, direction_v];
6921        let batch_vars = batch_workspace.alloc_slice_fill_with(K, |axis| {
6922            DynamicOneSeedBatch::seed_directions(values[axis], axis, K, &batch_workspace, |lane| {
6923                directions[lane][axis]
6924            })
6925        });
6926        let dynamic_batch = expression(batch_vars);
6927        assert_eq!(dynamic_batch.lanes(), 2);
6928        for lane in 0..2 {
6929            let expected = if lane == 0 {
6930                &fixed_third
6931            } else {
6932                &fixed_third_v
6933            };
6934            for a in 0..K {
6935                for b in 0..K {
6936                    close(
6937                        dynamic_batch.contracted_third(lane)[a * K + b],
6938                        expected[a][b],
6939                    );
6940                }
6941            }
6942        }
6943
6944        let fixed_two: Vec<FixedRuntimeJet<TwoSeed<K>, K>> = (0..K)
6945            .map(|axis| FixedRuntimeJet {
6946                inner: TwoSeed::seed(values[axis], axis, direction_u[axis], direction_v[axis]),
6947            })
6948            .collect();
6949        let arena_two = DynamicJetArena::new();
6950        let dynamic_two: Vec<DynamicTwoSeed<'_>> = (0..K)
6951            .map(|axis| {
6952                DynamicTwoSeed::seed(
6953                    values[axis],
6954                    axis,
6955                    direction_u[axis],
6956                    direction_v[axis],
6957                    K,
6958                    &arena_two,
6959                )
6960            })
6961            .collect();
6962        let fixed_fourth = expression(&fixed_two).into_inner().contracted_fourth();
6963        let dynamic_fourth = expression(&dynamic_two);
6964        for a in 0..K {
6965            for b in 0..K {
6966                close(
6967                    dynamic_fourth.contracted_fourth()[a * K + b],
6968                    fixed_fourth[a][b],
6969                );
6970            }
6971        }
6972
6973        let fixed_two_swapped: Vec<FixedRuntimeJet<TwoSeed<K>, K>> = (0..K)
6974            .map(|axis| {
6975                FixedRuntimeJet::from_inner(TwoSeed::seed(
6976                    values[axis],
6977                    axis,
6978                    direction_v[axis],
6979                    direction_u[axis],
6980                ))
6981            })
6982            .collect();
6983        let fixed_fourth_swapped = expression(&fixed_two_swapped)
6984            .into_inner()
6985            .contracted_fourth();
6986        let pair_workspace = DynamicJetBatchWorkspace::new(2);
6987        let direction_pairs = [(direction_u, direction_v), (direction_v, direction_u)];
6988        let pair_vars = pair_workspace.alloc_slice_fill_with(K, |axis| {
6989            DynamicTwoSeedBatch::seed_direction_pairs(
6990                values[axis],
6991                axis,
6992                K,
6993                &pair_workspace,
6994                |lane| (direction_pairs[lane].0[axis], direction_pairs[lane].1[axis]),
6995            )
6996        });
6997        let dynamic_pair_batch = expression(pair_vars);
6998        assert_eq!(dynamic_pair_batch.lanes(), 2);
6999        for lane in 0..2 {
7000            let expected = if lane == 0 {
7001                &fixed_fourth
7002            } else {
7003                &fixed_fourth_swapped
7004            };
7005            for a in 0..K {
7006                for b in 0..K {
7007                    close(
7008                        dynamic_pair_batch.contracted_fourth(lane)[a * K + b],
7009                        expected[a][b],
7010                    );
7011                }
7012            }
7013        }
7014    }
7015
7016    #[test]
7017    fn dynamic_jet_arena_compacts_fragmented_high_water_932() {
7018        const WORDS_PER_ALLOCATION: usize = 1 << 17;
7019        const ALLOCATIONS: usize = 6;
7020
7021        let mut arena = DynamicJetArena::new();
7022        for lane in 0..ALLOCATIONS {
7023            let allocation = arena.alloc_slice_fill_with(WORDS_PER_ALLOCATION, |_| lane as u64);
7024            std::hint::black_box(allocation);
7025        }
7026        let fragmented_high_water = arena.allocated_bytes();
7027
7028        arena.reset();
7029        let compact_high_water = arena.allocated_bytes();
7030        assert!(
7031            compact_high_water >= fragmented_high_water,
7032            "compacted arena must retain the complete fragmented tape"
7033        );
7034
7035        for lane in 0..ALLOCATIONS {
7036            let allocation = arena.alloc_slice_fill_with(WORDS_PER_ALLOCATION, |_| lane as u64);
7037            std::hint::black_box(allocation);
7038        }
7039        assert_eq!(
7040            arena.allocated_bytes(),
7041            compact_high_water,
7042            "equal replay must fit in the compacted chunk"
7043        );
7044
7045        arena.reset();
7046        assert_eq!(
7047            arena.allocated_bytes(),
7048            compact_high_water,
7049            "stable reset must retain the compacted chunk"
7050        );
7051    }
7052}
7053
7054#[cfg(test)]
7055mod batch_tests {
7056    //! SIMD row-batching oracle: prove [`Order2Batch<K>`] (4 rows in
7057    //! `wide::f64x4` lanes) is `to_bits`-identical, on every value/gradient/
7058    //! Hessian channel, to the production [`Order2<K>`] evaluated per row — and
7059    //! that the new scalar field [`Order2Lane<f64, K>`] is too. Composing the two
7060    //! claims, batch lane `i` reproduces the production scalar for row `i` bit
7061    //! for bit, so the 4× throughput is a free lunch (no result change).
7062
7063    use super::{
7064        JetScalar, Lane, OneSeed, OneSeedBatch, OneSeedLane, Order2, Order2Batch, Order2Lane,
7065        TwoSeed, TwoSeedBatch, TwoSeedLane,
7066    };
7067    // The scalar-field algebra (`value`, `add`, …) lives on the shared `JetField`
7068    // base now, so the concrete-typed channel reads below need it in scope.
7069    use crate::nested_dual::JetField;
7070
7071    /// The ops the witness row expression needs, so ONE generic body evaluates
7072    /// at the production [`Order2<K>`], the new scalar [`Order2Lane<f64, K>`],
7073    /// and the batched [`Order2Batch<K>`].
7074    trait RowAlg<const K: usize>: Copy {
7075        fn constant(c: f64) -> Self;
7076        fn add(&self, o: &Self) -> Self;
7077        fn sub(&self, o: &Self) -> Self;
7078        fn mul(&self, o: &Self) -> Self;
7079        fn scale(&self, s: f64) -> Self;
7080        fn exp(&self) -> Self;
7081        fn sqrt(&self) -> Self;
7082        fn recip(&self) -> Self;
7083    }
7084
7085    impl<const K: usize> RowAlg<K> for Order2<K> {
7086        fn constant(c: f64) -> Self {
7087            <Self as JetScalar<K>>::constant(c)
7088        }
7089        fn add(&self, o: &Self) -> Self {
7090            crate::nested_dual::JetField::add(self, o)
7091        }
7092        fn sub(&self, o: &Self) -> Self {
7093            crate::nested_dual::JetField::sub(self, o)
7094        }
7095        fn mul(&self, o: &Self) -> Self {
7096            crate::nested_dual::JetField::mul(self, o)
7097        }
7098        fn scale(&self, s: f64) -> Self {
7099            crate::nested_dual::JetField::scale(self, s)
7100        }
7101        fn exp(&self) -> Self {
7102            JetScalar::exp(self)
7103        }
7104        fn sqrt(&self) -> Self {
7105            JetScalar::sqrt(self)
7106        }
7107        fn recip(&self) -> Self {
7108            JetScalar::recip(self)
7109        }
7110    }
7111
7112    impl<L: Lane, const K: usize> RowAlg<K> for Order2Lane<L, K> {
7113        fn constant(c: f64) -> Self {
7114            Order2Lane::constant(L::splat(c))
7115        }
7116        fn add(&self, o: &Self) -> Self {
7117            Order2Lane::add(self, o)
7118        }
7119        fn sub(&self, o: &Self) -> Self {
7120            Order2Lane::sub(self, o)
7121        }
7122        fn mul(&self, o: &Self) -> Self {
7123            Order2Lane::mul(self, o)
7124        }
7125        fn scale(&self, s: f64) -> Self {
7126            Order2Lane::scale(self, s)
7127        }
7128        fn exp(&self) -> Self {
7129            Order2Lane::exp(self)
7130        }
7131        fn sqrt(&self) -> Self {
7132            Order2Lane::sqrt(self)
7133        }
7134        fn recip(&self) -> Self {
7135            Order2Lane::recip(self)
7136        }
7137    }
7138
7139    /// A dense witness row expression touching every algebra op (mul, add, sub,
7140    /// scale, exp, sqrt, recip) over ALL `K` primaries, so the gradient and the
7141    /// full `K×K` Hessian are dense (no trivially-zero channel). All transcend.
7142    /// arguments are kept finite/positive: `sqrt(s²+1) > 0`, `recip(exp+2) > 0`.
7143    fn row_expr<const K: usize, A: RowAlg<K>>(p: &[A; K]) -> A {
7144        let mut s = A::constant(0.3);
7145        for a in 0..K {
7146            let b = (a + 1) % K;
7147            s = s.add(&p[a].mul(&p[b]).scale(0.1 + 0.05 * a as f64));
7148        }
7149        let e = s.exp();
7150        let r = s.mul(&s).add(&A::constant(1.0)).sqrt();
7151        let denom = e.add(&A::constant(2.0));
7152        e.mul(&r).sub(&s.scale(0.5)).mul(&denom.recip())
7153    }
7154
7155    /// xorshift64 → `f64` in `[-1, 1)`.
7156    fn rand_unit(state: &mut u64) -> f64 {
7157        let mut x = *state;
7158        x ^= x << 13;
7159        x ^= x >> 7;
7160        x ^= x << 17;
7161        *state = x;
7162        let u = (x >> 11) as f64 / ((1u64 << 53) as f64); // [0, 1)
7163        2.0 * u - 1.0
7164    }
7165
7166    /// Returns the number of (batch, row) pairs whose every channel was
7167    /// verified bit-identical, so the caller can assert the expected total ran.
7168    fn check_k<const K: usize>(state: &mut u64, batches: usize) -> usize {
7169        let mut verified_rows = 0usize;
7170        for _ in 0..batches {
7171            // Four independent rows of K primary values.
7172            let rows: [[f64; K]; 4] =
7173                std::array::from_fn(|_| std::array::from_fn(|_| rand_unit(state)));
7174
7175            // Production ground truth, evaluated per row at Order2<K>.
7176            let prod: [Order2<K>; 4] = std::array::from_fn(|r| {
7177                let p: [Order2<K>; K] = std::array::from_fn(|a| Order2::variable(rows[r][a], a));
7178                row_expr(&p)
7179            });
7180
7181            // New scalar field (Order2Lane<f64>), per row.
7182            let scal: [Order2Lane<f64, K>; 4] = std::array::from_fn(|r| {
7183                let p: [Order2Lane<f64, K>; K] =
7184                    std::array::from_fn(|a| Order2Lane::variable(rows[r][a], a));
7185                row_expr(&p)
7186            });
7187
7188            // Batched: 4 rows packed into f64x4 lanes, ONE vector pass.
7189            let pbatch: [Order2Batch<K>; K] = std::array::from_fn(|a| {
7190                let packed = wide::f64x4::new([rows[0][a], rows[1][a], rows[2][a], rows[3][a]]);
7191                Order2Batch::variable(packed, a)
7192            });
7193            let batch = row_expr(&pbatch);
7194
7195            for r in 0..4 {
7196                let g = prod[r].0;
7197                // Order2Lane<f64> == Order2<K> (bit-identical scalar field).
7198                assert_eq!(scal[r].v.to_bits(), g.v.to_bits(), "K={K} scalar v");
7199                // Batch lane r == Order2<K> for row r.
7200                let lr = batch.lane(r).0;
7201                assert_eq!(lr.v.to_bits(), g.v.to_bits(), "K={K} batch lane {r} v");
7202                for a in 0..K {
7203                    assert_eq!(
7204                        scal[r].g[a].to_bits(),
7205                        g.g[a].to_bits(),
7206                        "K={K} scalar g[{a}]"
7207                    );
7208                    assert_eq!(
7209                        lr.g[a].to_bits(),
7210                        g.g[a].to_bits(),
7211                        "K={K} batch lane {r} g[{a}]"
7212                    );
7213                    for b in 0..K {
7214                        assert_eq!(
7215                            scal[r].h[a][b].to_bits(),
7216                            g.h[a][b].to_bits(),
7217                            "K={K} scalar h[{a}][{b}]"
7218                        );
7219                        assert_eq!(
7220                            lr.h[a][b].to_bits(),
7221                            g.h[a][b].to_bits(),
7222                            "K={K} batch lane {r} h[{a}][{b}]"
7223                        );
7224                    }
7225                }
7226                verified_rows += 1;
7227            }
7228        }
7229        verified_rows
7230    }
7231
7232    /// ≥2000 random 4-row batches per K, across K ∈ {2,3,4,9}: every channel of
7233    /// every lane is `to_bits`-identical to the production scalar per row.
7234    #[test]
7235    fn batch_lanes_bit_identical_to_scalar_per_row() {
7236        let mut state = 0x9E37_79B9_7F4A_7C15_u64;
7237        let mut verified = 0usize;
7238        verified += check_k::<2>(&mut state, 2000);
7239        verified += check_k::<3>(&mut state, 2000);
7240        verified += check_k::<4>(&mut state, 2000);
7241        verified += check_k::<9>(&mut state, 2000);
7242        // 4 K-values × 2000 batches × 4 packed rows each, all bit-identical.
7243        assert_eq!(verified, 4 * 2000 * 4, "every batch row must be verified");
7244    }
7245
7246    // ── One-/two-seed lane oracles ──────────────────────────────────────────
7247    //
7248    // The same dense `row_expr` witness program runs over the SEEDED directional
7249    // scalars: the scalar `OneSeed`/`TwoSeed` per row, the `f64`-lane re-type
7250    // (`*SeedLane<f64>`), and the 4-rows-per-pass batch (`*SeedBatch`). The
7251    // headline claim is that the contracted-third / contracted-fourth channel of
7252    // every lane is `to_bits`-identical to the production scalar's per row.
7253
7254    impl<const K: usize> RowAlg<K> for OneSeed<K> {
7255        fn constant(c: f64) -> Self {
7256            <Self as JetScalar<K>>::constant(c)
7257        }
7258        fn add(&self, o: &Self) -> Self {
7259            crate::nested_dual::JetField::add(self, o)
7260        }
7261        fn sub(&self, o: &Self) -> Self {
7262            crate::nested_dual::JetField::sub(self, o)
7263        }
7264        fn mul(&self, o: &Self) -> Self {
7265            crate::nested_dual::JetField::mul(self, o)
7266        }
7267        fn scale(&self, s: f64) -> Self {
7268            crate::nested_dual::JetField::scale(self, s)
7269        }
7270        fn exp(&self) -> Self {
7271            JetScalar::exp(self)
7272        }
7273        fn sqrt(&self) -> Self {
7274            JetScalar::sqrt(self)
7275        }
7276        fn recip(&self) -> Self {
7277            JetScalar::recip(self)
7278        }
7279    }
7280
7281    impl<L: Lane, const K: usize> RowAlg<K> for OneSeedLane<L, K> {
7282        fn constant(c: f64) -> Self {
7283            OneSeedLane::constant(L::splat(c))
7284        }
7285        fn add(&self, o: &Self) -> Self {
7286            OneSeedLane::add(self, o)
7287        }
7288        fn sub(&self, o: &Self) -> Self {
7289            OneSeedLane::sub(self, o)
7290        }
7291        fn mul(&self, o: &Self) -> Self {
7292            OneSeedLane::mul(self, o)
7293        }
7294        fn scale(&self, s: f64) -> Self {
7295            OneSeedLane::scale(self, s)
7296        }
7297        fn exp(&self) -> Self {
7298            OneSeedLane::exp(self)
7299        }
7300        fn sqrt(&self) -> Self {
7301            OneSeedLane::sqrt(self)
7302        }
7303        fn recip(&self) -> Self {
7304            OneSeedLane::recip(self)
7305        }
7306    }
7307
7308    impl<const K: usize> RowAlg<K> for TwoSeed<K> {
7309        fn constant(c: f64) -> Self {
7310            <Self as JetScalar<K>>::constant(c)
7311        }
7312        fn add(&self, o: &Self) -> Self {
7313            crate::nested_dual::JetField::add(self, o)
7314        }
7315        fn sub(&self, o: &Self) -> Self {
7316            crate::nested_dual::JetField::sub(self, o)
7317        }
7318        fn mul(&self, o: &Self) -> Self {
7319            crate::nested_dual::JetField::mul(self, o)
7320        }
7321        fn scale(&self, s: f64) -> Self {
7322            crate::nested_dual::JetField::scale(self, s)
7323        }
7324        fn exp(&self) -> Self {
7325            JetScalar::exp(self)
7326        }
7327        fn sqrt(&self) -> Self {
7328            JetScalar::sqrt(self)
7329        }
7330        fn recip(&self) -> Self {
7331            JetScalar::recip(self)
7332        }
7333    }
7334
7335    impl<L: Lane, const K: usize> RowAlg<K> for TwoSeedLane<L, K> {
7336        fn constant(c: f64) -> Self {
7337            TwoSeedLane::constant(L::splat(c))
7338        }
7339        fn add(&self, o: &Self) -> Self {
7340            TwoSeedLane::add(self, o)
7341        }
7342        fn sub(&self, o: &Self) -> Self {
7343            TwoSeedLane::sub(self, o)
7344        }
7345        fn mul(&self, o: &Self) -> Self {
7346            TwoSeedLane::mul(self, o)
7347        }
7348        fn scale(&self, s: f64) -> Self {
7349            TwoSeedLane::scale(self, s)
7350        }
7351        fn exp(&self) -> Self {
7352            TwoSeedLane::exp(self)
7353        }
7354        fn sqrt(&self) -> Self {
7355            TwoSeedLane::sqrt(self)
7356        }
7357        fn recip(&self) -> Self {
7358            TwoSeedLane::recip(self)
7359        }
7360    }
7361
7362    fn check_oneseed<const K: usize>(state: &mut u64, batches: usize) -> usize {
7363        let mut rows_checked = 0;
7364        for _ in 0..batches {
7365            let rows: [[f64; K]; 4] =
7366                std::array::from_fn(|_| std::array::from_fn(|_| rand_unit(state)));
7367            // Per-row ε-direction.
7368            let u: [[f64; K]; 4] =
7369                std::array::from_fn(|_| std::array::from_fn(|_| rand_unit(state)));
7370
7371            // Production ground truth (scalar OneSeed per row).
7372            let prod: [OneSeed<K>; 4] = std::array::from_fn(|r| {
7373                let p: [OneSeed<K>; K] =
7374                    std::array::from_fn(|a| OneSeed::seed_direction(rows[r][a], a, u[r][a]));
7375                row_expr(&p)
7376            });
7377
7378            // f64-lane re-type per row.
7379            let scal: [OneSeedLane<f64, K>; 4] = std::array::from_fn(|r| {
7380                let p: [OneSeedLane<f64, K>; K] =
7381                    std::array::from_fn(|a| OneSeedLane::seed_direction(rows[r][a], a, u[r][a]));
7382                row_expr(&p)
7383            });
7384
7385            // 4-rows-per-pass batch.
7386            let pbatch: [OneSeedBatch<K>; K] = std::array::from_fn(|a| {
7387                let val = wide::f64x4::new([rows[0][a], rows[1][a], rows[2][a], rows[3][a]]);
7388                let uu = wide::f64x4::new([u[0][a], u[1][a], u[2][a], u[3][a]]);
7389                OneSeedBatch::seed_direction(val, a, uu)
7390            });
7391            let batch = row_expr(&pbatch);
7392
7393            for r in 0..4 {
7394                let want = prod[r].contracted_third();
7395                let got_scal = scal[r].contracted_third();
7396                let got_batch = batch.lane(r).contracted_third();
7397                // Value channel too (sanity that the base program agrees).
7398                assert_eq!(
7399                    scal[r].base.v.to_bits(),
7400                    prod[r].base.value().to_bits(),
7401                    "OneSeed K={K} scalar value"
7402                );
7403                assert_eq!(
7404                    batch.lane(r).base.value().to_bits(),
7405                    prod[r].base.value().to_bits(),
7406                    "OneSeed K={K} batch lane {r} value"
7407                );
7408                for a in 0..K {
7409                    for b in 0..K {
7410                        assert_eq!(
7411                            got_scal[a][b].to_bits(),
7412                            want[a][b].to_bits(),
7413                            "OneSeed K={K} scalar third[{a}][{b}]"
7414                        );
7415                        assert_eq!(
7416                            got_batch[a][b].to_bits(),
7417                            want[a][b].to_bits(),
7418                            "OneSeed K={K} batch lane {r} third[{a}][{b}]"
7419                        );
7420                    }
7421                }
7422                rows_checked += 1;
7423            }
7424        }
7425        rows_checked
7426    }
7427
7428    fn check_twoseed<const K: usize>(state: &mut u64, batches: usize) -> usize {
7429        let mut rows_checked = 0;
7430        for _ in 0..batches {
7431            let rows: [[f64; K]; 4] =
7432                std::array::from_fn(|_| std::array::from_fn(|_| rand_unit(state)));
7433            let u: [[f64; K]; 4] =
7434                std::array::from_fn(|_| std::array::from_fn(|_| rand_unit(state)));
7435            let v: [[f64; K]; 4] =
7436                std::array::from_fn(|_| std::array::from_fn(|_| rand_unit(state)));
7437
7438            let prod: [TwoSeed<K>; 4] = std::array::from_fn(|r| {
7439                let p: [TwoSeed<K>; K] =
7440                    std::array::from_fn(|a| TwoSeed::seed(rows[r][a], a, u[r][a], v[r][a]));
7441                row_expr(&p)
7442            });
7443
7444            let scal: [TwoSeedLane<f64, K>; 4] = std::array::from_fn(|r| {
7445                let p: [TwoSeedLane<f64, K>; K] =
7446                    std::array::from_fn(|a| TwoSeedLane::seed(rows[r][a], a, u[r][a], v[r][a]));
7447                row_expr(&p)
7448            });
7449
7450            let pbatch: [TwoSeedBatch<K>; K] = std::array::from_fn(|a| {
7451                let val = wide::f64x4::new([rows[0][a], rows[1][a], rows[2][a], rows[3][a]]);
7452                let uu = wide::f64x4::new([u[0][a], u[1][a], u[2][a], u[3][a]]);
7453                let vv = wide::f64x4::new([v[0][a], v[1][a], v[2][a], v[3][a]]);
7454                TwoSeedBatch::seed(val, a, uu, vv)
7455            });
7456            let batch = row_expr(&pbatch);
7457
7458            for r in 0..4 {
7459                let want = prod[r].contracted_fourth();
7460                let got_scal = scal[r].contracted_fourth();
7461                let got_batch = batch.lane(r).contracted_fourth();
7462                assert_eq!(
7463                    scal[r].base.v.to_bits(),
7464                    prod[r].base.value().to_bits(),
7465                    "TwoSeed K={K} scalar value"
7466                );
7467                assert_eq!(
7468                    batch.lane(r).base.value().to_bits(),
7469                    prod[r].base.value().to_bits(),
7470                    "TwoSeed K={K} batch lane {r} value"
7471                );
7472                for a in 0..K {
7473                    for b in 0..K {
7474                        assert_eq!(
7475                            got_scal[a][b].to_bits(),
7476                            want[a][b].to_bits(),
7477                            "TwoSeed K={K} scalar fourth[{a}][{b}]"
7478                        );
7479                        assert_eq!(
7480                            got_batch[a][b].to_bits(),
7481                            want[a][b].to_bits(),
7482                            "TwoSeed K={K} batch lane {r} fourth[{a}][{b}]"
7483                        );
7484                    }
7485                }
7486                rows_checked += 1;
7487            }
7488        }
7489        rows_checked
7490    }
7491
7492    /// ≥2000 random 4-row batches per K, across K ∈ {2,3,4,9}: the
7493    /// contracted-third channel of every `OneSeedLane` lane is `to_bits`-identical
7494    /// to the production [`OneSeed`] per row.
7495    #[test]
7496    fn oneseed_lanes_contracted_third_bit_identical() {
7497        let mut state = 0x1234_5678_9ABC_DEF0_u64;
7498        let batches = 2000;
7499        let rows_checked = check_oneseed::<2>(&mut state, batches)
7500            + check_oneseed::<3>(&mut state, batches)
7501            + check_oneseed::<4>(&mut state, batches)
7502            + check_oneseed::<9>(&mut state, batches);
7503        // 4 widths × `batches` batches × 4 rows each: a silently empty inner
7504        // loop would leave this at zero instead of passing as a no-op.
7505        assert_eq!(rows_checked, 4 * batches * 4);
7506    }
7507
7508    /// ≥2000 random 4-row batches per K, across K ∈ {2,3,4,9}: the
7509    /// contracted-fourth channel of every `TwoSeedLane` lane is `to_bits`-identical
7510    /// to the production [`TwoSeed`] per row.
7511    #[test]
7512    fn twoseed_lanes_contracted_fourth_bit_identical() {
7513        let mut state = 0x0FED_CBA9_8765_4321_u64;
7514        let batches = 2000;
7515        let rows_checked = check_twoseed::<2>(&mut state, batches)
7516            + check_twoseed::<3>(&mut state, batches)
7517            + check_twoseed::<4>(&mut state, batches)
7518            + check_twoseed::<9>(&mut state, batches);
7519        // 4 widths × `batches` batches × 4 rows each: a silently empty inner
7520        // loop would leave this at zero instead of passing as a no-op.
7521        assert_eq!(rows_checked, 4 * batches * 4);
7522    }
7523}
7524
7525#[cfg(test)]
7526mod unit_tests {
7527    use super::{
7528        DynamicJetArena, DynamicOrder2, JetScalar, OneSeed, Order1, Order2, RuntimeJetScalar,
7529        filtered_implicit_solve_scalar,
7530    };
7531    use crate::nested_dual::{Dual2, JetField};
7532
7533    // A deliberately mixed beta/family program. Its family derivatives below
7534    // are analytic scalar programs, so these checks have no finite-difference
7535    // truncation or step-size oracle.
7536    fn family_program<const K: usize, S: JetScalar<K>>(x: &S, y: &S, theta: &S) -> S {
7537        let xy = x.mul(y);
7538        let exponential = theta.mul(&xy).exp();
7539        let theta_squared_x_squared = theta.mul(theta).mul(&x.mul(x)).scale(0.375);
7540        let theta_y_cubed = theta.mul(&y.mul(y).mul(y)).scale(-0.2);
7541        exponential
7542            .add(&theta_squared_x_squared)
7543            .add(&theta_y_cubed)
7544    }
7545
7546    fn analytic_family_first<const K: usize, S: JetScalar<K>>(x: &S, y: &S, theta: &S) -> S {
7547        let xy = x.mul(y);
7548        let exponential = theta.mul(&xy).exp();
7549        xy.mul(&exponential)
7550            .add(&theta.mul(&x.mul(x)).scale(0.75))
7551            .add(&y.mul(y).mul(y).scale(-0.2))
7552    }
7553
7554    fn analytic_family_second<const K: usize, S: JetScalar<K>>(x: &S, y: &S, theta: &S) -> S {
7555        let xy = x.mul(y);
7556        let exponential = theta.mul(&xy).exp();
7557        xy.mul(&xy).mul(&exponential).add(&x.mul(x).scale(0.75))
7558    }
7559
7560    fn assert_channel_close(actual: f64, expected: f64, channel: &str) {
7561        let tolerance = 256.0 * f64::EPSILON * (1.0 + actual.abs().max(expected.abs()));
7562        assert!(
7563            (actual - expected).abs() <= tolerance,
7564            "{channel}: actual={actual:.17e}, expected={expected:.17e}, tolerance={tolerance:.3e}"
7565        );
7566    }
7567
7568    fn assert_order2_channels<const K: usize>(
7569        actual: &Order2<K>,
7570        expected: &Order2<K>,
7571        prefix: &str,
7572    ) {
7573        assert_channel_close(actual.value(), expected.value(), &format!("{prefix}.value"));
7574        for a in 0..K {
7575            assert_channel_close(actual.g()[a], expected.g()[a], &format!("{prefix}.g[{a}]"));
7576            for b in 0..K {
7577                assert_channel_close(
7578                    actual.h()[a][b],
7579                    expected.h()[a][b],
7580                    &format!("{prefix}.h[{a}][{b}]"),
7581                );
7582            }
7583        }
7584    }
7585
7586    /// #932 correctness and release racer for the runtime shaped-value
7587    /// primitives. The comparison arm is the exact constant-synthesis idiom
7588    /// removed from the SLS wiggle paths: a unary composition whose derivative
7589    /// stack is identically zero. Both arms allocate the same runtime-width
7590    /// output shape from a warm-reset arena; the only intended difference is
7591    /// whether known-zero channels are written directly or computed by walking
7592    /// the Faà di Bruno composition.
7593    #[test]
7594    fn runtime_shaped_value_primitives_are_exact_and_skip_constant_composition_932() {
7595        use std::time::Instant;
7596
7597        const K: usize = 48;
7598        let arena = DynamicJetArena::new();
7599        let variable = DynamicOrder2::variable(0.75, 7, K, &arena);
7600
7601        let constant = variable.constant_like(1.25);
7602        assert_eq!(constant.value().to_bits(), 1.25_f64.to_bits());
7603        assert_eq!(constant.dimension(), K);
7604        assert!(constant.g().iter().all(|&channel| channel == 0.0));
7605        assert!(constant.h().iter().all(|&channel| channel == 0.0));
7606
7607        let replaced = variable.with_value(-2.5);
7608        assert_eq!(replaced.value().to_bits(), (-2.5_f64).to_bits());
7609        assert_eq!(replaced.g(), variable.g());
7610        assert_eq!(replaced.h(), variable.h());
7611
7612        fn best_ns(mut evaluate: impl FnMut(f64) -> f64, iterations: usize) -> f64 {
7613            let mut best = f64::INFINITY;
7614            for _ in 0..5 {
7615                let mut checksum = 0.0_f64;
7616                let started = Instant::now();
7617                for _ in 0..iterations {
7618                    checksum += evaluate(0.75 + checksum * 1e-18);
7619                }
7620                assert!(checksum.is_finite());
7621                best = best.min(started.elapsed().as_secs_f64());
7622            }
7623            best * 1e9 / iterations as f64
7624        }
7625
7626        let iterations = if cfg!(debug_assertions) { 200 } else { 20_000 };
7627        let mut direct_arena = DynamicJetArena::new();
7628        let direct_ns = best_ns(
7629            |value| {
7630                direct_arena.reset();
7631                let variable = DynamicOrder2::variable(value, 7, K, &direct_arena);
7632                let constant = variable.constant_like(1.25);
7633                constant.value() + constant.g()[K - 1] + constant.h()[K * K - 1]
7634            },
7635            iterations,
7636        );
7637        let mut composed_arena = DynamicJetArena::new();
7638        let composed_ns = best_ns(
7639            |value| {
7640                composed_arena.reset();
7641                let variable = DynamicOrder2::variable(value, 7, K, &composed_arena);
7642                let constant = variable.compose_unary([1.25, 0.0, 0.0, 0.0, 0.0]);
7643                constant.value() + constant.g()[K - 1] + constant.h()[K * K - 1]
7644            },
7645            iterations,
7646        );
7647        eprintln!(
7648            "RUNTIME-CONSTANT-932 dimension={K} direct={direct_ns:.2} ns \
7649             composed={composed_ns:.2} ns composed_over_direct={:.6}",
7650            composed_ns / direct_ns,
7651        );
7652    }
7653
7654    /// The outer first/second channels carry the family derivative while each
7655    /// inner `Order2` independently carries beta value/gradient/Hessian.
7656    #[test]
7657    fn dual2_order2_extracts_exact_family_value_gradient_hessian_channels() {
7658        const K: usize = 2;
7659        let x0 = 0.7;
7660        let y0 = -0.45;
7661        let theta0 = 0.6;
7662        let x = <Dual2<Order2<K>> as JetScalar<K>>::variable(x0, 0);
7663        let y = <Dual2<Order2<K>> as JetScalar<K>>::variable(y0, 1);
7664        let theta = Dual2 {
7665            v: Order2::constant(theta0),
7666            g: Order2::constant(1.0),
7667            h: Order2::constant(0.0),
7668        };
7669
7670        let actual = family_program(&x, &y, &theta);
7671        let reference_x = Order2::variable(x0, 0);
7672        let reference_y = Order2::variable(y0, 1);
7673        let reference_theta = Order2::constant(theta0);
7674        let expected_first = analytic_family_first(&reference_x, &reference_y, &reference_theta);
7675        let expected_second = analytic_family_second(&reference_x, &reference_y, &reference_theta);
7676
7677        assert_order2_channels(&actual.g, &expected_first, "family_first");
7678        assert_order2_channels(&actual.h, &expected_second, "family_second");
7679    }
7680
7681    /// `g.eps.h[a][b]` is the directional beta drift of the family-Hessian
7682    /// channel, obtained by exact nested AD rather than differencing Hessians.
7683    #[test]
7684    fn dual2_oneseed_extracts_exact_family_hessian_drift() {
7685        const K: usize = 2;
7686        let x0 = 0.7;
7687        let y0 = -0.45;
7688        let theta0 = 0.6;
7689        let direction = [0.3, -0.8];
7690
7691        let mut x = <Dual2<OneSeed<K>> as JetScalar<K>>::variable(x0, 0);
7692        let mut y = <Dual2<OneSeed<K>> as JetScalar<K>>::variable(y0, 1);
7693        x.v.eps = Order2::constant(direction[0]);
7694        y.v.eps = Order2::constant(direction[1]);
7695        let theta = Dual2 {
7696            v: OneSeed::constant(theta0),
7697            g: OneSeed::constant(1.0),
7698            h: OneSeed::constant(0.0),
7699        };
7700
7701        let actual = family_program(&x, &y, &theta);
7702        let reference_x = OneSeed::seed_direction(x0, 0, direction[0]);
7703        let reference_y = OneSeed::seed_direction(y0, 1, direction[1]);
7704        let reference_theta = OneSeed::constant(theta0);
7705        let expected = analytic_family_first(&reference_x, &reference_y, &reference_theta);
7706
7707        assert_order2_channels(&actual.g.eps, &expected.eps, "family_first_drift");
7708    }
7709
7710    // ── Order2 direct property tests ─────────────────────────────────────────
7711
7712    /// `Order2::constant(c)` carries value `c` and zero everywhere else.
7713    #[test]
7714    fn order2_constant_has_zero_derivatives() {
7715        let s = Order2::<3>::constant(7.5);
7716        assert_eq!(s.value(), 7.5);
7717        for a in 0..3 {
7718            assert_eq!(s.g()[a], 0.0, "grad[{a}] should be zero");
7719            for b in 0..3 {
7720                assert_eq!(s.h()[a][b], 0.0, "hess[{a}][{b}] should be zero");
7721            }
7722        }
7723    }
7724
7725    /// `Order2::variable(x, axis)` has unit gradient in slot `axis` and zero Hessian.
7726    #[test]
7727    fn order2_variable_has_unit_gradient_in_seeded_slot() {
7728        let x = -2.5_f64;
7729        let s = Order2::<4>::variable(x, 2);
7730        assert_eq!(s.value(), x);
7731        for a in 0..4 {
7732            let expected_g = if a == 2 { 1.0 } else { 0.0 };
7733            assert_eq!(s.g()[a], expected_g, "grad[{a}]");
7734            for b in 0..4 {
7735                assert_eq!(s.h()[a][b], 0.0, "hess[{a}][{b}] should be zero");
7736            }
7737        }
7738    }
7739
7740    /// `Order2::add` sums gradient channels; `sub` is the inverse on gradients.
7741    /// Uses integer-valued primaries so the value roundtrip is also exact.
7742    #[test]
7743    fn order2_add_sub_roundtrip() {
7744        let p = Order2::<2>::variable(3.0, 0);
7745        let q = Order2::<2>::variable(2.0, 1);
7746        let pq = crate::nested_dual::JetField::add(&p, &q);
7747        // value = 3 + 2 = 5
7748        assert_eq!(pq.value(), 5.0, "add value");
7749        let back = crate::nested_dual::JetField::sub(&pq, &q);
7750        // (p + q) - q gradient should equal p's gradient exactly
7751        for a in 0..2 {
7752            assert_eq!(back.g()[a], p.g()[a], "grad[{a}] roundtrip");
7753        }
7754    }
7755
7756    /// `Order2::mul` of two variables satisfies the Leibniz product rule:
7757    ///   ∂(p·q)/∂p = q,  ∂(p·q)/∂q = p,  ∂²(p·q)/∂p∂q = 1.
7758    #[test]
7759    fn order2_mul_satisfies_leibniz_rule() {
7760        let pv = 3.0_f64;
7761        let qv = -2.0_f64;
7762        let p = Order2::<2>::variable(pv, 0);
7763        let q = Order2::<2>::variable(qv, 1);
7764        let pq = crate::nested_dual::JetField::mul(&p, &q);
7765        assert_eq!(pq.value(), pv * qv, "value = p·q");
7766        assert_eq!(pq.g()[0], qv, "∂(p·q)/∂p = q");
7767        assert_eq!(pq.g()[1], pv, "∂(p·q)/∂q = p");
7768        assert_eq!(pq.h()[0][1], 1.0, "∂²(p·q)/∂p∂q = 1");
7769        assert_eq!(pq.h()[1][0], 1.0, "∂²(p·q)/∂q∂p = 1 (symmetric)");
7770        assert_eq!(pq.h()[0][0], 0.0, "∂²(p·q)/∂p² = 0");
7771        assert_eq!(pq.h()[1][1], 0.0, "∂²(p·q)/∂q² = 0");
7772    }
7773
7774    /// `Order2::scale(s)` multiplies every channel by `s`.
7775    #[test]
7776    fn order2_scale_multiplies_all_channels() {
7777        let p = Order2::<2>::variable(4.0, 0);
7778        let s = 2.5_f64;
7779        let ps = crate::nested_dual::JetField::scale(&p, s);
7780        assert_eq!(ps.value(), 4.0 * s);
7781        assert_eq!(ps.g()[0], 1.0 * s);
7782        assert_eq!(ps.g()[1], 0.0);
7783    }
7784
7785    /// `Order2::exp` at a constant has value `e^c`, gradient `e^c * g`, Hessian `e^c * (g⊗g + H)`.
7786    /// At a seeded variable `p₀`, the first derivative is `e^{p₀}` and second is `e^{p₀}`.
7787    #[test]
7788    fn order2_exp_derivative_stack_correct() {
7789        let p0 = 1.0_f64;
7790        let p = Order2::<1>::variable(p0, 0);
7791        let ep = JetScalar::exp(&p);
7792        let e = p0.exp();
7793        assert!((ep.value() - e).abs() < 1e-15, "exp value");
7794        assert!((ep.g()[0] - e).abs() < 1e-15, "d/dp exp(p) = exp(p)");
7795        assert!((ep.h()[0][0] - e).abs() < 1e-15, "d²/dp² exp(p) = exp(p)");
7796    }
7797
7798    /// `Order2::ln` at a seeded variable: d/dp ln(p) = 1/p, d²/dp² ln(p) = -1/p².
7799    #[test]
7800    fn order2_ln_derivative_stack_correct() {
7801        let p0 = 2.0_f64;
7802        let p = Order2::<1>::variable(p0, 0);
7803        let lnp = JetScalar::ln(&p);
7804        assert!((lnp.value() - p0.ln()).abs() < 1e-15, "ln value");
7805        assert!((lnp.g()[0] - 1.0 / p0).abs() < 1e-15, "d/dp ln(p) = 1/p");
7806        assert!(
7807            (lnp.h()[0][0] - (-1.0 / (p0 * p0))).abs() < 1e-15,
7808            "d²/dp² ln(p) = -1/p²"
7809        );
7810    }
7811
7812    #[test]
7813    fn dynamic_order2_ln_uses_runtime_scalar_derivative_stack() {
7814        let p0 = 2.0_f64;
7815        let arena = DynamicJetArena::new();
7816        let p = DynamicOrder2::variable(p0, 0, 1, &arena);
7817        let lnp = RuntimeJetScalar::ln(&p);
7818        assert!((lnp.value() - p0.ln()).abs() < 1e-15, "ln value");
7819        assert!((lnp.g()[0] - 1.0 / p0).abs() < 1e-15, "d/dp ln(p) = 1/p");
7820        assert!(
7821            (lnp.h_at(0, 0) - (-1.0 / (p0 * p0))).abs() < 1e-15,
7822            "d²/dp² ln(p) = -1/p²"
7823        );
7824    }
7825
7826    /// `exp` and `ln` are mutual inverses: `ln(exp(p)).value() == p` at the scalar.
7827    #[test]
7828    fn order2_exp_ln_roundtrip_at_value() {
7829        let p0 = 0.8_f64;
7830        let p = Order2::<1>::variable(p0, 0);
7831        let roundtrip = JetScalar::ln(&JetScalar::exp(&p));
7832        assert!((roundtrip.value() - p0).abs() < 1e-14, "ln(exp(p)) ≈ p");
7833    }
7834
7835    // ── Order1 tests ─────────────────────────────────────────────────────────
7836
7837    /// `Order1::constant` carries the correct value with all-zero gradient.
7838    #[test]
7839    fn order1_constant_has_zero_gradient() {
7840        let s = Order1::<3>::constant(-5.0);
7841        assert_eq!(s.value(), -5.0);
7842        for a in 0..3 {
7843            assert_eq!(s.g()[a], 0.0, "g[{a}] should be zero");
7844        }
7845    }
7846
7847    /// `Order1::variable(x, axis)` has unit gradient only in `axis`.
7848    #[test]
7849    fn order1_variable_has_unit_gradient_in_seeded_slot() {
7850        let s = Order1::<3>::variable(2.0, 1);
7851        assert_eq!(s.value(), 2.0);
7852        assert_eq!(s.g()[0], 0.0);
7853        assert_eq!(s.g()[1], 1.0);
7854        assert_eq!(s.g()[2], 0.0);
7855    }
7856
7857    /// `Order1::mul` satisfies the product rule (value and gradient, no Hessian).
7858    #[test]
7859    fn order1_mul_satisfies_product_rule() {
7860        let pv = 3.0_f64;
7861        let qv = -2.0_f64;
7862        let p = Order1::<2>::variable(pv, 0);
7863        let q = Order1::<2>::variable(qv, 1);
7864        let pq = crate::nested_dual::JetField::mul(&p, &q);
7865        assert_eq!(pq.value(), pv * qv);
7866        assert_eq!(pq.g()[0], qv, "∂(p·q)/∂p = q");
7867        assert_eq!(pq.g()[1], pv, "∂(p·q)/∂q = p");
7868    }
7869
7870    /// `Order1::exp` carries the correct value and gradient `e^{p₀}`.
7871    #[test]
7872    fn order1_exp_has_correct_value_and_gradient() {
7873        let p0 = 0.5_f64;
7874        let p = Order1::<2>::variable(p0, 0);
7875        let ep = JetScalar::exp(&p);
7876        let e = p0.exp();
7877        assert!((ep.value() - e).abs() < 1e-15, "exp value");
7878        assert!((ep.g()[0] - e).abs() < 1e-15, "d/dp exp(p)");
7879        assert_eq!(ep.g()[1], 0.0, "irrelevant gradient slot is zero");
7880    }
7881
7882    /// `Order1` and `Order2` agree on value and gradient for the same expression.
7883    #[test]
7884    fn order1_and_order2_agree_on_value_and_gradient() {
7885        let p0 = 1.3_f64;
7886        let q0 = -0.7_f64;
7887        // evaluate (p * q + p).exp() at (p0, q0)
7888        let p1 = Order1::<2>::variable(p0, 0);
7889        let q1 = Order1::<2>::variable(q0, 1);
7890        let expr1 = JetScalar::exp(&crate::nested_dual::JetField::add(
7891            &crate::nested_dual::JetField::mul(&p1, &q1),
7892            &p1,
7893        ));
7894
7895        let p2 = Order2::<2>::variable(p0, 0);
7896        let q2 = Order2::<2>::variable(q0, 1);
7897        let expr2 = JetScalar::exp(&crate::nested_dual::JetField::add(
7898            &crate::nested_dual::JetField::mul(&p2, &q2),
7899            &p2,
7900        ));
7901
7902        assert!(
7903            (expr1.value() - expr2.value()).abs() < 1e-14,
7904            "value mismatch"
7905        );
7906        for a in 0..2 {
7907            assert!(
7908                (expr1.g()[a] - expr2.g()[a]).abs() < 1e-14,
7909                "gradient[{a}] mismatch"
7910            );
7911        }
7912    }
7913
7914    // ── filtered_implicit_solve_scalar ────────────────────────────────────────
7915
7916    /// Lift the trivial linear constraint F(a, θ) = a - θ = 0 through `Order2<1>`.
7917    /// The exact lifted jet is a(θ) = θ, so value=θ₀, gradient=1.
7918    #[test]
7919    fn filtered_implicit_solve_linear_constraint_gives_exact_jet() {
7920        let theta0 = 3.0_f64;
7921        let theta = Order2::<1>::variable(theta0, 0);
7922        // a0 = theta0, F_a = 1, inv_fa = 1; 2 iters suffice for Order2.
7923        let a = filtered_implicit_solve_scalar::<1, Order2<1>>(theta0, 1.0, 2, |a_jet| {
7924            crate::nested_dual::JetField::sub(a_jet, &theta)
7925        });
7926        assert!((a.value() - theta0).abs() < 1e-14, "value = theta0");
7927        // da/dtheta = 1 (identity)
7928        assert!((a.g()[0] - 1.0).abs() < 1e-14, "gradient = 1");
7929        // d²a/dtheta² = 0 (linear)
7930        assert!(a.h()[0][0].abs() < 1e-14, "hessian = 0");
7931    }
7932
7933    /// `filtered_implicit_solve_scalar` on a quadratic constraint F(a,θ)=a²-θ=0
7934    /// with primal root a₀=√θ₀, giving da/dθ = 1/(2√θ₀), d²a/dθ² = -1/(4θ₀^{3/2}).
7935    #[test]
7936    fn filtered_implicit_solve_quadratic_constraint_matches_analytic_derivatives() {
7937        let theta0 = 4.0_f64;
7938        let a0 = theta0.sqrt();
7939        let inv_fa = 1.0 / (2.0 * a0);
7940        let theta = Order2::<1>::variable(theta0, 0);
7941        // F(a,theta) = a*a - theta
7942        let a = filtered_implicit_solve_scalar::<1, Order2<1>>(a0, inv_fa, 2, |a_jet| {
7943            let aa = crate::nested_dual::JetField::mul(a_jet, a_jet);
7944            crate::nested_dual::JetField::sub(&aa, &theta)
7945        });
7946        let tol = 1e-12;
7947        assert!((a.value() - a0).abs() < tol, "value = sqrt(theta0)");
7948        let expected_g = 0.5 / a0;
7949        assert!(
7950            (a.g()[0] - expected_g).abs() < tol,
7951            "da/dtheta = 1/(2*sqrt)"
7952        );
7953        let expected_h = -0.25 / (theta0 * a0);
7954        assert!(
7955            (a.h()[0][0] - expected_h).abs() < tol,
7956            "d2a/dtheta2 = -1/(4*theta^1.5)"
7957        );
7958    }
7959}