Skip to main content

gam_models/gamlss/gaussian/
joint_psi.rs

1// Real concern-organized submodule of the gamlss family stack.
2// Cross-module items are re-exported flat through the parent (`gamlss.rs`),
3// so `use super::*;` makes the sibling-concern symbols this module references
4// resolve through the parent namespace.
5use super::*;
6use gam_row_macros::row_atom;
7
8// Stable local coordinates for the Gaussian location-scale row NLL. At the
9// expansion point `delta_mu = delta_eta = 0`, the log-b link gives
10// `sigma(eta + delta_eta) / sigma(eta) = (1-kappa) + kappa*exp(delta_eta)`.
11// The perturbed standardized residual is therefore
12// `(standardized_residual - delta_mu*inv_sigma) / scale_ratio`. Every runtime
13// constant is already certified by `gaussian_diagonal_row_kernel`, so this one
14// expression retains the production extreme-value semantics while build-time
15// differentiation emits exact observed H, contracted t3, and contracted t4.
16row_atom! {
17    fn gaussian_normalized_row [generic, order2_at_zero, third_at_zero, fourth_at_zero](
18        delta_mu,
19        delta_eta;
20        obs_weight: f64,
21        standardized_residual: f64,
22        inv_sigma: f64,
23        kappa: f64
24    ) {
25        obs_weight * ln((1.0 - kappa) + kappa * exp(delta_eta))
26            + 0.5
27                * obs_weight
28                * (standardized_residual - delta_mu * inv_sigma)
29                * (standardized_residual - delta_mu * inv_sigma)
30                / ((1.0 - kappa) + kappa * exp(delta_eta))
31                / ((1.0 - kappa) + kappa * exp(delta_eta))
32    }
33}
34
35pub(crate) struct LocationScaleJointPsiDirection {
36    pub(crate) block_idx: usize,
37    pub(crate) local_idx: usize,
38    pub(crate) x_primary_psi: PsiDesignMap,
39    pub(crate) x_ls_psi: PsiDesignMap,
40    pub(crate) z_primary_psi: Array1<f64>,
41    pub(crate) z_ls_psi: Array1<f64>,
42}
43
44pub(crate) struct LocationScaleJointPsiSecondDrifts {
45    pub(crate) x_primary_ab_action: Option<CustomFamilyPsiSecondDesignAction>,
46    pub(crate) x_ls_ab_action: Option<CustomFamilyPsiSecondDesignAction>,
47    pub(crate) x_primary_ab: Option<Array2<f64>>,
48    pub(crate) x_ls_ab: Option<Array2<f64>>,
49    pub(crate) z_primary_ab: Array1<f64>,
50    pub(crate) z_ls_ab: Array1<f64>,
51}
52
53/// Shared interface that the Gaussian and Binomial location-scale families (and
54/// their wiggle variants) expose to the unified joint ψ workspace.
55///
56/// The four families are structurally identical at the workspace level: each
57/// owns two dense block designs (location + log-scale), produces a per-ψ
58/// direction, and assembles second-order ψ terms and a ψ-Hessian directional
59/// derivative from those parts. They differ only in (1) the family-name
60/// fragment in the dense-designs error message, and (2) whether an optional
61/// Horvitz–Thompson outer-row subsample is threaded into the per-row weight
62/// arrays (Gaussian does; Binomial ignores it and runs the full-data exact
63/// path). All four produce the same concrete
64/// [`LocationScaleJointPsiDirection`], so the direction is a plain type here,
65/// not an associated one. This single trait gives the generic
66/// [`LocationScaleJointPsiWorkspace`] one dispatch surface; each family's impl
67/// is a thin delegation to inherent methods it already owns.
68pub(crate) trait LocationScaleJointPsiFamily: Clone + Send + Sync + 'static {
69
70    /// Family-name fragment used in the workspace's dense-designs error
71    /// message so the originating family stays visible after unification.
72    const LABEL: &'static str;
73
74    fn ws_policy(&self) -> &gam_runtime::resource::ResourcePolicy;
75
76    fn ws_exact_joint_dense_block_designs<'a>(
77        &'a self,
78        specs: Option<&'a [ParameterBlockSpec]>,
79    ) -> Result<Option<(Cow<'a, Array2<f64>>, Cow<'a, Array2<f64>>)>, String>;
80
81    fn ws_psi_direction(
82        &self,
83        block_states: &[ParameterBlockState],
84        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
85        psi_index: usize,
86        design_loc: &Array2<f64>,
87        design_scale: &Array2<f64>,
88        policy: &gam_runtime::resource::ResourcePolicy,
89    ) -> Result<Option<LocationScaleJointPsiDirection>, String>;
90
91    fn ws_psi_second_order_terms_from_parts(
92        &self,
93        block_states: &[ParameterBlockState],
94        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
95        psi_a: &LocationScaleJointPsiDirection,
96        psi_b: &LocationScaleJointPsiDirection,
97        design_loc: &Array2<f64>,
98        design_scale: &Array2<f64>,
99        subsample: Option<&[crate::outer_subsample::WeightedOuterRow]>,
100    ) -> Result<ExactNewtonJointPsiSecondOrderTerms, String>;
101
102    fn ws_psi_hessian_directional_from_parts(
103        &self,
104        block_states: &[ParameterBlockState],
105        psi_dir: &LocationScaleJointPsiDirection,
106        d_beta_flat: &Array1<f64>,
107        design_loc: &Array2<f64>,
108        design_scale: &Array2<f64>,
109        subsample: Option<&[crate::outer_subsample::WeightedOuterRow]>,
110    ) -> Result<Array2<f64>, String>;
111
112    /// Wire label for this family's primary (first) parameter block: `"mu"` for
113    /// the Gaussian families, `"threshold"` for the Binomial ones. The shared
114    /// direction/drift helpers name it in their diagnostics, so it is the one
115    /// token that distinguished four otherwise byte-identical copies.
116    const PRIMARY_LABEL: &'static str;
117
118    /// Index of the primary parameter block (each family's `BLOCK_MU` /
119    /// `BLOCK_T`). Zero for every family that exists today; carried as a named
120    /// const rather than a literal so a family that reorders its blocks cannot
121    /// silently disagree with the helpers it delegates to.
122    const PRIMARY_BLOCK: usize;
123
124    /// Index of the log-scale block. One for all four families; defaulted so a
125    /// family only restates it if that ever stops being true.
126    const LOG_SIGMA_BLOCK: usize = 1;
127
128    /// Parameter-block count: two for the location-scale families, three for
129    /// the wiggle variants, which append the wiggle block.
130    const N_BLOCKS: usize;
131
132    /// Fitted response row count, used to size the per-row ψ parts.
133    fn ws_n_obs(&self) -> usize;
134
135    /// Per-ψ joint direction from the already-realised block designs.
136    ///
137    /// Shared default (#2470): the four families' copies of this body were
138    /// identical up to `PRIMARY_BLOCK`, `N_BLOCKS`, `LABEL`, `PRIMARY_LABEL`,
139    /// the local name of the primary design, and the order of two fields in
140    /// the returned struct literal. Nothing else differed, so there is one
141    /// body now and the consts above carry the difference.
142    fn exact_newton_joint_psi_direction(
143        &self,
144        block_states: &[ParameterBlockState],
145        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
146        psi_index: usize,
147        x_primary: &Array2<f64>,
148        x_ls: &Array2<f64>,
149        policy: &gam_runtime::resource::ResourcePolicy,
150    ) -> Result<Option<LocationScaleJointPsiDirection>, String> {
151        let Some(parts) = locscale_joint_psi_direction_parts(
152            block_states,
153            derivative_blocks,
154            psi_index,
155            self.ws_n_obs(),
156            x_primary.ncols(),
157            x_ls.ncols(),
158            Self::PRIMARY_BLOCK,
159            Self::LOG_SIGMA_BLOCK,
160            Self::N_BLOCKS,
161            Self::LABEL,
162            Self::PRIMARY_LABEL,
163            policy,
164        )?
165        else {
166            return Ok(None);
167        };
168        Ok(Some(LocationScaleJointPsiDirection {
169            block_idx: parts.block_idx,
170            local_idx: parts.local_idx,
171            z_primary_psi: parts.primary_z,
172            z_ls_psi: parts.log_sigma_z,
173            x_primary_psi: parts.primary_psi,
174            x_ls_psi: parts.log_sigma_psi,
175        }))
176    }
177
178    /// Second-order ψ design drifts from the already-realised block designs.
179    ///
180    /// Shared default (#2470): the four copies differed only in `LABEL`,
181    /// `PRIMARY_BLOCK`, `PRIMARY_LABEL` and the local name of the primary
182    /// design — a byte-identical body otherwise.
183    fn exact_newton_joint_psisecond_design_drifts(
184        &self,
185        block_states: &[ParameterBlockState],
186        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
187        psi_a: &LocationScaleJointPsiDirection,
188        psi_b: &LocationScaleJointPsiDirection,
189        x_primary: &Array2<f64>,
190        x_ls: &Array2<f64>,
191    ) -> Result<LocationScaleJointPsiSecondDrifts, String> {
192        locscale_joint_psisecond_design_drifts(
193            block_states,
194            derivative_blocks,
195            psi_a,
196            psi_b,
197            LocScalePsiDriftConfig {
198                n: self.ws_n_obs(),
199                p_primary: x_primary.ncols(),
200                p_log_sigma: x_ls.ncols(),
201                primary_block_idx: Self::PRIMARY_BLOCK,
202                log_sigma_block_idx: Self::LOG_SIGMA_BLOCK,
203                family_name: Self::LABEL,
204                primary_label: Self::PRIMARY_LABEL,
205                policy: self.ws_policy(),
206            },
207        )
208    }
209
210    /// ψ-Hessian directional derivative from already-realised block designs.
211    ///
212    /// Shared default (#2470). The four copies differed only in the local name
213    /// of the primary design and in a trailing `None`: the Gaussian
214    /// location-scale family's inherent `_from_parts` takes an
215    /// outer-subsample argument and the other three do not, so only that copy
216    /// passed one. Routing through [`Self::ws_psi_hessian_directional_from_parts`]
217    /// — which every family already implements and which normalises that
218    /// argument — makes the arity uniform without adding a subsample parameter
219    /// to the three inherent bodies that deliberately lack one (see the
220    /// full-data rationale on the wiggle impls below).
221    fn exact_newton_joint_psihessian_directional_derivative_from_designs(
222        &self,
223        block_states: &[ParameterBlockState],
224        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
225        psi_index: usize,
226        d_beta_flat: &Array1<f64>,
227        x_primary: &Array2<f64>,
228        x_ls: &Array2<f64>,
229    ) -> Result<Option<Array2<f64>>, String> {
230        let Some(dir_a) = self.exact_newton_joint_psi_direction(
231            block_states,
232            derivative_blocks,
233            psi_index,
234            x_primary,
235            x_ls,
236            self.ws_policy(),
237        )?
238        else {
239            return Ok(None);
240        };
241        Ok(Some(self.ws_psi_hessian_directional_from_parts(
242            block_states,
243            &dir_a,
244            d_beta_flat,
245            x_primary,
246            x_ls,
247            None,
248        )?))
249    }
250
251    /// ψ-Hessian directional derivative for a realised spec list.
252    ///
253    /// Shared default (#2470). Three of the four families already took
254    /// `derivative_blocks` directly; `GaussianLocationScaleFamily` alone still
255    /// took a `CustomFamilyHyperLayout` and re-checked `family_axis_count()`
256    /// before forwarding `design_derivative_blocks()`. That inner check was
257    /// unreachable-when-true: this method's only caller, on every family, is
258    /// that family's own `CustomFamily` entry point, which performs the
259    /// byte-identical check immediately before forwarding. The surviving guard
260    /// is the outer one; the signature here is the three-family majority.
261    fn exact_newton_joint_psihessian_directional_derivative_for_specs(
262        &self,
263        block_states: &[ParameterBlockState],
264        specs: &[ParameterBlockSpec],
265        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
266        psi_index: usize,
267        d_beta_flat: &Array1<f64>,
268    ) -> Result<Option<Array2<f64>>, String> {
269        let Some((x_primary, x_ls)) = self.ws_exact_joint_dense_block_designs(Some(specs))? else {
270            return Ok(None);
271        };
272        self.exact_newton_joint_psihessian_directional_derivative_from_designs(
273            block_states,
274            derivative_blocks,
275            psi_index,
276            d_beta_flat,
277            &x_primary,
278            &x_ls,
279        )
280    }
281}
282
283impl LocationScaleJointPsiFamily for GaussianLocationScaleFamily {
284    const LABEL: &'static str = "GaussianLocationScaleFamily";
285
286    const PRIMARY_LABEL: &'static str = "mu";
287    const PRIMARY_BLOCK: usize = Self::BLOCK_MU;
288    const N_BLOCKS: usize = 2;
289
290    fn ws_n_obs(&self) -> usize {
291        self.y.len()
292    }
293
294    fn ws_policy(&self) -> &gam_runtime::resource::ResourcePolicy {
295        &self.policy
296    }
297
298    fn ws_exact_joint_dense_block_designs<'a>(
299        &'a self,
300        specs: Option<&'a [ParameterBlockSpec]>,
301    ) -> Result<Option<(Cow<'a, Array2<f64>>, Cow<'a, Array2<f64>>)>, String> {
302        self.exact_joint_dense_block_designs(specs)
303    }
304
305    fn ws_psi_direction(
306        &self,
307        block_states: &[ParameterBlockState],
308        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
309        psi_index: usize,
310        design_loc: &Array2<f64>,
311        design_scale: &Array2<f64>,
312        policy: &gam_runtime::resource::ResourcePolicy,
313    ) -> Result<Option<LocationScaleJointPsiDirection>, String> {
314        self.exact_newton_joint_psi_direction(
315            block_states,
316            derivative_blocks,
317            psi_index,
318            design_loc,
319            design_scale,
320            policy,
321        )
322    }
323
324    fn ws_psi_second_order_terms_from_parts(
325        &self,
326        block_states: &[ParameterBlockState],
327        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
328        psi_a: &LocationScaleJointPsiDirection,
329        psi_b: &LocationScaleJointPsiDirection,
330        design_loc: &Array2<f64>,
331        design_scale: &Array2<f64>,
332        subsample: Option<&[crate::outer_subsample::WeightedOuterRow]>,
333    ) -> Result<ExactNewtonJointPsiSecondOrderTerms, String> {
334        self.exact_newton_joint_psisecond_order_terms_from_parts(
335            block_states,
336            derivative_blocks,
337            psi_a,
338            psi_b,
339            design_loc,
340            design_scale,
341            subsample,
342        )
343    }
344
345    fn ws_psi_hessian_directional_from_parts(
346        &self,
347        block_states: &[ParameterBlockState],
348        psi_dir: &LocationScaleJointPsiDirection,
349        d_beta_flat: &Array1<f64>,
350        design_loc: &Array2<f64>,
351        design_scale: &Array2<f64>,
352        subsample: Option<&[crate::outer_subsample::WeightedOuterRow]>,
353    ) -> Result<Array2<f64>, String> {
354        self.exact_newton_joint_psihessian_directional_derivative_from_parts(
355            block_states,
356            psi_dir,
357            d_beta_flat,
358            design_loc,
359            design_scale,
360            subsample,
361        )
362    }
363}
364
365impl LocationScaleJointPsiFamily for GaussianLocationScaleWiggleFamily {
366    const LABEL: &'static str = "GaussianLocationScaleWiggleFamily";
367
368    const PRIMARY_LABEL: &'static str = "mu";
369    const PRIMARY_BLOCK: usize = Self::BLOCK_MU;
370    const N_BLOCKS: usize = 3;
371
372    fn ws_n_obs(&self) -> usize {
373        self.y.len()
374    }
375
376    fn ws_policy(&self) -> &gam_runtime::resource::ResourcePolicy {
377        &self.policy
378    }
379
380    fn ws_exact_joint_dense_block_designs<'a>(
381        &'a self,
382        specs: Option<&'a [ParameterBlockSpec]>,
383    ) -> Result<Option<(Cow<'a, Array2<f64>>, Cow<'a, Array2<f64>>)>, String> {
384        self.exact_joint_dense_block_designs(specs)
385    }
386
387    fn ws_psi_direction(
388        &self,
389        block_states: &[ParameterBlockState],
390        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
391        psi_index: usize,
392        design_loc: &Array2<f64>,
393        design_scale: &Array2<f64>,
394        policy: &gam_runtime::resource::ResourcePolicy,
395    ) -> Result<Option<LocationScaleJointPsiDirection>, String> {
396        self.exact_newton_joint_psi_direction(
397            block_states,
398            derivative_blocks,
399            psi_index,
400            design_loc,
401            design_scale,
402            policy,
403        )
404    }
405
406    fn ws_psi_second_order_terms_from_parts(
407        &self,
408        block_states: &[ParameterBlockState],
409        derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
410        psi_a: &LocationScaleJointPsiDirection,
411        psi_b: &LocationScaleJointPsiDirection,
412        design_loc: &Array2<f64>,
413        design_scale: &Array2<f64>,
414        subsample_rows: Option<&[crate::outer_subsample::WeightedOuterRow]>,
415    ) -> Result<ExactNewtonJointPsiSecondOrderTerms, String> {
416        // This path runs full-data (see the rationale below), but a mask that
417        // cannot even index into the design is a caller defect rather than a
418        // deliberate full-data choice.
419        if let Some(rows) = subsample_rows {
420            if rows.len() > design_loc.nrows() {
421                return Err(format!(
422                    "ws_psi_second_order_terms_from_parts: {} weighted outer rows for a \
423                     {}-row design",
424                    rows.len(),
425                    design_loc.nrows()
426                ));
427            }
428        }
429        // Wiggle ψ path: full-data exact (= trivially unbiased). The outer
430        // score remains unbiased without touching this path: HT-unbiased LL
431        // (`log_likelihood_only_with_options`) + HT-unbiased ρ-Hessian
432        // (`exact_newton_joint_hessian_workspace_with_options`) +
433        // exact-unbiased ψ (this path) = unbiased.
434        //
435        // This comment used to say the wiggle from-parts functions inline 30+
436        // per-row coefficient arrays instead of packing them, and to name
437        // "refactor them into `WiggleJointPsi{First,Second}Weights` mirroring
438        // `GaussianJointPsi{First,Second}Weights`" as the prerequisite for
439        // broadening HT subsampling here. THAT PREREQUISITE IS DONE, under
440        // different names, and the stale text sent at least one refactor pass
441        // chasing work that already existed:
442        //
443        //   * second order — `GlsWiggleSecondDirCoeffs` (wiggle.rs:359, 33
444        //     fields: `coeff_{mm,ml,ll}_{base,u,v,uv}`, `mean_wiggle_*`,
445        //     `gradient_{mu,ls}_*`, `hessian_{ml,mm}_*`), built by
446        //     `gls_wiggle_second_directional_coeffs` and destructured whole at
447        //     the top of `exact_newton_joint_psisecond_order_terms_from_parts`.
448        //   * first order — `GlsWiggleFirstDirCoeffs` (wiggle.rs:395),
449        //     destructured at wiggle.rs:793.
450        //
451        // So the gaussian wiggle second-order body is now one struct
452        // destructure and zero loose `mut` row accumulators, and a single
453        // `apply_ht_mask_wiggle*` helper over those two structs is writable
454        // today. What is NOT settled is whether masking the struct fields is
455        // SUFFICIENT: the body also derives row arrays outside the struct
456        // (`q_{a,b,ab}`, `s1_*`, `g2_*`, `s_mu*`) and matrix quantities
457        // (`basis_*`, `basis1_*`) from the direction vectors, and whether those
458        // are HT-neutral or need masking too is the actual open question.
459        //
460        // The binomial wiggle path is a different shape and is NOT covered by
461        // the above: its `exact_newton_joint_psisecond_order_terms_from_parts`
462        // is 734 lines with 21 loose `mut` row accumulators and no struct
463        // destructure, even though `BinomialWiggle{First,Second}DirectionalRows`
464        // (11 and 15 fields) exist and are referenced elsewhere in that file.
465        self.exact_newton_joint_psisecond_order_terms_from_parts(
466            block_states,
467            derivative_blocks,
468            psi_a,
469            psi_b,
470            design_loc,
471            design_scale,
472        )
473    }
474
475    fn ws_psi_hessian_directional_from_parts(
476        &self,
477        block_states: &[ParameterBlockState],
478        psi_dir: &LocationScaleJointPsiDirection,
479        d_beta_flat: &Array1<f64>,
480        design_loc: &Array2<f64>,
481        design_scale: &Array2<f64>,
482        subsample_rows: Option<&[crate::outer_subsample::WeightedOuterRow]>,
483    ) -> Result<Array2<f64>, String> {
484        // Same full-data rationale, same shape precondition on the mask.
485        if let Some(rows) = subsample_rows {
486            if rows.len() > design_loc.nrows() {
487                return Err(format!(
488                    "ws_psi_hessian_directional_from_parts: {} weighted outer rows for a \
489                     {}-row design",
490                    rows.len(),
491                    design_loc.nrows()
492                ));
493            }
494        }
495        // Same rationale as `ws_psi_second_order_terms_from_parts` above:
496        // the wiggle ψ-Hessian directional-derivative function also inlines
497        // dozens of per-row arrays. Full-data is exact (= trivially
498        // unbiased), so the total outer score remains unbiased.
499        self.exact_newton_joint_psihessian_directional_derivative_from_parts(
500            block_states,
501            psi_dir,
502            d_beta_flat,
503            design_loc,
504            design_scale,
505        )
506    }
507}
508
509/// Generic joint exact-Newton ψ workspace shared by every location-scale
510/// family (Gaussian / Binomial, with or without a wiggle block) via the
511/// [`LocationScaleJointPsiFamily`] trait.
512///
513/// The workspace owns the two dense block designs as `Arc<Array2<f64>>` (the
514/// per-family `ws_exact_joint_dense_block_designs` hands back a `Cow`, which is
515/// materialized once here), the per-ψ direction cache, and an optional
516/// Horvitz–Thompson outer-row subsample. When the subsample is `Some`, every
517/// per-row weight array produced inside the second-order ψ Hessian and the
518/// ψ-Hessian directional-derivative computations is masked: each sampled row's
519/// contribution is scaled by `WeightedOuterRow.weight = 1/π_i` and non-sampled
520/// rows are zeroed. Because every downstream assembly is row-linear in those
521/// arrays, the resulting ψ score and ψ Hessian remain unbiased estimators of
522/// the full-data quantities. Families that do not thread the subsample (the
523/// Binomial families) construct with `new` and the field stays `None`.
524pub(crate) struct LocationScaleJointPsiWorkspace<F: LocationScaleJointPsiFamily> {
525    pub(crate) family: F,
526    pub(crate) block_states: Vec<ParameterBlockState>,
527    pub(crate) derivative_blocks: Vec<Vec<CustomFamilyBlockPsiDerivative>>,
528    pub(crate) design_loc: Arc<Array2<f64>>,
529    pub(crate) design_scale: Arc<Array2<f64>>,
530    pub(crate) psi_directions: ExactNewtonJointPsiDirectCache<LocationScaleJointPsiDirection>,
531    pub(crate) outer_score_subsample: Option<Arc<crate::outer_subsample::OuterScoreSubsample>>,
532}
533
534impl<F: LocationScaleJointPsiFamily> LocationScaleJointPsiWorkspace<F> {
535    pub(crate) fn new(
536        family: F,
537        block_states: Vec<ParameterBlockState>,
538        specs: &[ParameterBlockSpec],
539        derivative_blocks: Vec<Vec<CustomFamilyBlockPsiDerivative>>,
540    ) -> Result<Self, String> {
541        Self::new_with_subsample(family, block_states, specs, derivative_blocks, None)
542    }
543
544    pub(crate) fn new_with_subsample(
545        family: F,
546        block_states: Vec<ParameterBlockState>,
547        specs: &[ParameterBlockSpec],
548        derivative_blocks: Vec<Vec<CustomFamilyBlockPsiDerivative>>,
549        outer_score_subsample: Option<Arc<crate::outer_subsample::OuterScoreSubsample>>,
550    ) -> Result<Self, String> {
551        let Some((design_loc, design_scale)) =
552            family.ws_exact_joint_dense_block_designs(Some(specs))?
553        else {
554            return Err(GamlssError::UnsupportedConfiguration {
555                reason: format!(
556                    "{} exact joint psi workspace requires dense block designs",
557                    F::LABEL,
558                ),
559            }
560            .into());
561        };
562        let design_loc = shared_dense_arc(design_loc.as_ref());
563        let design_scale = shared_dense_arc(design_scale.as_ref());
564        let psi_dim = derivative_blocks.iter().map(Vec::len).sum();
565        Ok(Self {
566            family,
567            block_states,
568            derivative_blocks,
569            design_loc,
570            design_scale,
571            psi_directions: ExactNewtonJointPsiDirectCache::new(psi_dim),
572            outer_score_subsample,
573        })
574    }
575
576    pub(crate) fn psi_direction(
577        &self,
578        psi_index: usize,
579    ) -> Result<Option<Arc<LocationScaleJointPsiDirection>>, String> {
580        self.psi_directions
581            .get_or_try_init(psi_index, || {
582                self.family
583                    .ws_psi_direction(
584                        &self.block_states,
585                        &self.derivative_blocks,
586                        psi_index,
587                        self.design_loc.as_ref(),
588                        self.design_scale.as_ref(),
589                        self.family.ws_policy(),
590                    )
591                    .map_err(gam_problem::CustomFamilyError::trial_point)
592            })
593            // Display boundary (gam#2689): this accessor's `String` is the
594            // family-local currency shared with `GamlssError`.
595            .map_err(|error| error.to_string())
596    }
597
598    pub(crate) fn subsample_rows(&self) -> Option<&[crate::outer_subsample::WeightedOuterRow]> {
599        self.outer_score_subsample
600            .as_ref()
601            .map(|s| s.rows.as_ref().as_slice())
602    }
603}
604
605impl<F> ExactNewtonJointPsiWorkspace for LocationScaleJointPsiWorkspace<F>
606where
607    F: LocationScaleJointPsiFamily,
608{
609    fn second_order_terms(
610        &self,
611        psi_i: usize,
612        psi_j: usize,
613    ) -> Result<Option<ExactNewtonJointPsiSecondOrderTerms>, String> {
614        let Some(dir_i) = self.psi_direction(psi_i)? else {
615            return Ok(None);
616        };
617        let Some(dir_j) = self.psi_direction(psi_j)? else {
618            return Ok(None);
619        };
620        Ok(Some(self.family.ws_psi_second_order_terms_from_parts(
621            &self.block_states,
622            &self.derivative_blocks,
623            dir_i.as_ref(),
624            dir_j.as_ref(),
625            self.design_loc.as_ref(),
626            self.design_scale.as_ref(),
627            self.subsample_rows(),
628        )?))
629    }
630
631    fn hessian_directional_derivative(
632        &self,
633        psi_index: usize,
634        d_beta_flat: &Array1<f64>,
635    ) -> Result<Option<gam_problem::DriftDerivResult>, String> {
636        let Some(dir) = self.psi_direction(psi_index)? else {
637            return Ok(None);
638        };
639        Ok(Some(gam_problem::DriftDerivResult::Dense(
640            self.family.ws_psi_hessian_directional_from_parts(
641                &self.block_states,
642                dir.as_ref(),
643                d_beta_flat,
644                self.design_loc.as_ref(),
645                self.design_scale.as_ref(),
646                self.subsample_rows(),
647            )?,
648        )))
649    }
650}
651
652pub(crate) type GaussianLocationScaleExactNewtonJointPsiWorkspace =
653    LocationScaleJointPsiWorkspace<GaussianLocationScaleFamily>;
654
655pub(crate) type GaussianLocationScaleWiggleExactNewtonJointPsiWorkspace =
656    LocationScaleJointPsiWorkspace<GaussianLocationScaleWiggleFamily>;
657
658#[derive(Clone)]
659pub struct GaussianJointRowScalars {
660    pub(crate) obs_weight: Array1<f64>,
661    /// Stable `(y - mu) / sigma` at the expansion point.
662    pub(crate) standardized_residual: Array1<f64>,
663    /// Stable `1 / sigma` at the expansion point.
664    pub(crate) inv_sigma: Array1<f64>,
665    /// κ = (dσ/dη_ls)/σ for the active sigma link.
666    /// The cross Hessian block H_{μ,ls} carries an overall κ factor and the
667    /// scale-scale block H_{ls,ls} carries κ².
668    pub(crate) kappa: Array1<f64>,
669}
670
671/// Production [`gam_math::jet_tower::RowProgram`] for the normalized Gaussian
672/// location-scale row NLL.
673///
674/// The program borrows the exact certified row constants consumed by the live
675/// observed-Hessian and directional-weight paths. Its generic evaluator and
676/// those specialized order-2/third/fourth paths are emitted from the same
677/// `gaussian_normalized_row` declaration, so the parity oracle cannot retain
678/// an independent copy of the likelihood expression.
679pub struct GaussianJointRowProgram<'a> {
680    rows: &'a GaussianJointRowScalars,
681}
682
683impl<'a> GaussianJointRowProgram<'a> {
684    /// Bind the generic row program to one certified production scalar batch.
685    pub fn new(rows: &'a GaussianJointRowScalars) -> Self {
686        Self { rows }
687    }
688
689    fn require_row(&self, row: usize) -> Result<(), String> {
690        if row >= self.rows.obs_weight.len() {
691            return Err(format!(
692                "GaussianJointRowProgram row {row} out of range for {} rows",
693                self.rows.obs_weight.len()
694            ));
695        }
696        Ok(())
697    }
698
699    /// Symbolically lowered value/gradient/Hessian for one certified row.
700    ///
701    /// The concrete sparsity bits are part of the generated function's type:
702    /// both score channels and all three packed Hessian channels are live.
703    #[inline(always)]
704    pub(crate) fn row_order2(
705        &self,
706        row: usize,
707    ) -> gam_math::jet_scalar::StaticOrder2Atom<2, 3, 3, 7> {
708        gaussian_normalized_row_order2_at_zero(
709            self.rows.obs_weight[row],
710            self.rows.standardized_residual[row],
711            self.rows.inv_sigma[row],
712            self.rows.kappa[row],
713        )
714    }
715
716    /// Symbolically lowered Hessian derivative in one predictor direction.
717    #[inline(always)]
718    pub(crate) fn row_third_contracted(&self, row: usize, direction: &[f64; 2]) -> [[f64; 2]; 2] {
719        gaussian_normalized_row_third_contracted_at_zero(
720            self.rows.obs_weight[row],
721            self.rows.standardized_residual[row],
722            self.rows.inv_sigma[row],
723            self.rows.kappa[row],
724            direction,
725        )
726    }
727
728    /// Symbolically lowered mixed derivative of the row Hessian.
729    #[inline(always)]
730    pub(crate) fn row_fourth_contracted(
731        &self,
732        row: usize,
733        direction_u: &[f64; 2],
734        direction_v: &[f64; 2],
735    ) -> [[f64; 2]; 2] {
736        gaussian_normalized_row_fourth_contracted_at_zero(
737            self.rows.obs_weight[row],
738            self.rows.standardized_residual[row],
739            self.rows.inv_sigma[row],
740            self.rows.kappa[row],
741            direction_u,
742            direction_v,
743        )
744    }
745}
746
747#[inline(always)]
748fn matrix_vector_2(matrix: &[[f64; 2]; 2], vector: &[f64; 2]) -> [f64; 2] {
749    [
750        matrix[0][0] * vector[0] + matrix[0][1] * vector[1],
751        matrix[1][0] * vector[0] + matrix[1][1] * vector[1],
752    ]
753}
754
755#[inline(always)]
756fn dot_2(left: &[f64; 2], right: &[f64; 2]) -> f64 {
757    left[0] * right[0] + left[1] * right[1]
758}
759
760#[inline(always)]
761fn add_vector_2(left: [f64; 2], right: [f64; 2]) -> [f64; 2] {
762    [left[0] + right[0], left[1] + right[1]]
763}
764
765#[inline(always)]
766fn add_matrix_2(left: [[f64; 2]; 2], right: [[f64; 2]; 2]) -> [[f64; 2]; 2] {
767    [
768        [left[0][0] + right[0][0], left[0][1] + right[0][1]],
769        [left[1][0] + right[1][0], left[1][1] + right[1][1]],
770    ]
771}
772
773/// One order of generated Gaussian row geometry, stored in neutral predictor
774/// coordinates. At order zero these are `(g, H)`; in a first/second tower they
775/// are the corresponding directional derivatives of `(g, H)`.
776pub(crate) struct GaussianRowChannels {
777    pub(crate) gradient_mu: Array1<f64>,
778    pub(crate) gradient_ls: Array1<f64>,
779    pub(crate) hessian_mm: Array1<f64>,
780    pub(crate) hessian_ml: Array1<f64>,
781    pub(crate) hessian_ll: Array1<f64>,
782}
783
784impl GaussianRowChannels {
785    fn zeros(n: usize) -> Self {
786        Self {
787            gradient_mu: Array1::zeros(n),
788            gradient_ls: Array1::zeros(n),
789            hessian_mm: Array1::zeros(n),
790            hessian_ml: Array1::zeros(n),
791            hessian_ll: Array1::zeros(n),
792        }
793    }
794}
795
796pub(crate) struct GaussianRowFirstTower {
797    pub(crate) base: GaussianRowChannels,
798    pub(crate) first: GaussianRowChannels,
799}
800
801pub(crate) struct GaussianRowSecondTower {
802    pub(crate) base: GaussianRowChannels,
803    pub(crate) first_a: GaussianRowChannels,
804    pub(crate) first_b: GaussianRowChannels,
805    pub(crate) second: GaussianRowChannels,
806}
807
808fn write_base_channels(
809    channels: &mut GaussianRowChannels,
810    row: usize,
811    atom: &gam_math::jet_scalar::StaticOrder2Atom<2, 3, 3, 7>,
812) -> [[f64; 2]; 2] {
813    let gradient = atom.gradient();
814    let hessian = [
815        [atom.hessian_at(0, 0), atom.hessian_at(0, 1)],
816        [atom.hessian_at(1, 0), atom.hessian_at(1, 1)],
817    ];
818    channels.gradient_mu[row] = gradient[0];
819    channels.gradient_ls[row] = gradient[1];
820    channels.hessian_mm[row] = hessian[0][0];
821    channels.hessian_ml[row] = hessian[0][1];
822    channels.hessian_ll[row] = hessian[1][1];
823    hessian
824}
825
826fn write_directional_channels(
827    channels: &mut GaussianRowChannels,
828    row: usize,
829    gradient: [f64; 2],
830    hessian: [[f64; 2]; 2],
831) {
832    channels.gradient_mu[row] = gradient[0];
833    channels.gradient_ls[row] = gradient[1];
834    channels.hessian_mm[row] = hessian[0][0];
835    channels.hessian_ml[row] = hessian[0][1];
836    channels.hessian_ll[row] = hessian[1][1];
837}
838
839/// Generated Gaussian row gradient and Hessian with no directional scratch.
840pub(crate) fn gaussian_row_channels(rows: &GaussianJointRowScalars) -> GaussianRowChannels {
841    let n = rows.obs_weight.len();
842    let program = GaussianJointRowProgram::new(rows);
843    let mut base = GaussianRowChannels::zeros(n);
844    for row in 0..n {
845        let atom = program.row_order2(row);
846        write_base_channels(&mut base, row, &atom);
847    }
848    base
849}
850
851/// Generated `(g, H)` plus its first derivative along a rowwise predictor
852/// direction. The row atom is evaluated once per row.
853pub(crate) fn gaussian_row_first_tower(
854    rows: &GaussianJointRowScalars,
855    direction_mu: &Array1<f64>,
856    direction_ls: &Array1<f64>,
857) -> GaussianRowFirstTower {
858    let n = rows.obs_weight.len();
859    let program = GaussianJointRowProgram::new(rows);
860    let mut base = GaussianRowChannels::zeros(n);
861    let mut first = GaussianRowChannels::zeros(n);
862    for row in 0..n {
863        let direction = [direction_mu[row], direction_ls[row]];
864        let atom = program.row_order2(row);
865        let hessian = write_base_channels(&mut base, row, &atom);
866        write_directional_channels(
867            &mut first,
868            row,
869            matrix_vector_2(&hessian, &direction),
870            program.row_third_contracted(row, &direction),
871        );
872    }
873    GaussianRowFirstTower { base, first }
874}
875
876/// Generated `(g, H)` tower through the mixed second derivative along two
877/// rowwise directions, including a possibly nonzero mixed predictor leg.
878pub(crate) fn gaussian_row_second_tower(
879    rows: &GaussianJointRowScalars,
880    direction_a_mu: &Array1<f64>,
881    direction_a_ls: &Array1<f64>,
882    direction_b_mu: &Array1<f64>,
883    direction_b_ls: &Array1<f64>,
884    direction_ab_mu: &Array1<f64>,
885    direction_ab_ls: &Array1<f64>,
886) -> GaussianRowSecondTower {
887    let n = rows.obs_weight.len();
888    let program = GaussianJointRowProgram::new(rows);
889    let mut base = GaussianRowChannels::zeros(n);
890    let mut first_a = GaussianRowChannels::zeros(n);
891    let mut first_b = GaussianRowChannels::zeros(n);
892    let mut second = GaussianRowChannels::zeros(n);
893    for row in 0..n {
894        let direction_a = [direction_a_mu[row], direction_a_ls[row]];
895        let direction_b = [direction_b_mu[row], direction_b_ls[row]];
896        let direction_ab = [direction_ab_mu[row], direction_ab_ls[row]];
897        let atom = program.row_order2(row);
898        let hessian = write_base_channels(&mut base, row, &atom);
899        let hessian_a = program.row_third_contracted(row, &direction_a);
900        let hessian_b = program.row_third_contracted(row, &direction_b);
901        write_directional_channels(
902            &mut first_a,
903            row,
904            matrix_vector_2(&hessian, &direction_a),
905            hessian_a,
906        );
907        write_directional_channels(
908            &mut first_b,
909            row,
910            matrix_vector_2(&hessian, &direction_b),
911            hessian_b,
912        );
913        write_directional_channels(
914            &mut second,
915            row,
916            add_vector_2(
917                matrix_vector_2(&hessian_a, &direction_b),
918                matrix_vector_2(&hessian, &direction_ab),
919            ),
920            add_matrix_2(
921                program.row_fourth_contracted(row, &direction_a, &direction_b),
922                program.row_third_contracted(row, &direction_ab),
923            ),
924        );
925    }
926    GaussianRowSecondTower {
927        base,
928        first_a,
929        first_b,
930        second,
931    }
932}
933
934impl gam_math::jet_tower::RowProgram<2> for GaussianJointRowProgram<'_> {
935    fn n_rows(&self) -> usize {
936        self.rows.obs_weight.len()
937    }
938
939    fn primaries(&self, row: usize) -> Result<[f64; 2], String> {
940        self.require_row(row)?;
941        Ok([0.0, 0.0])
942    }
943
944    fn eval<S: gam_math::jet_scalar::JetScalar<2>>(
945        &self,
946        row: usize,
947        p: &[S; 2],
948    ) -> Result<S, String> {
949        self.require_row(row)?;
950        Ok(gaussian_normalized_row(
951            &p[0],
952            &p[1],
953            self.rows.obs_weight[row],
954            self.rows.standardized_residual[row],
955            self.rows.inv_sigma[row],
956            self.rows.kappa[row],
957        ))
958    }
959}
960
961pub(crate) struct GaussianJointPsiFirstWeights {
962    pub(crate) objective_psirow: Array1<f64>,
963    pub(crate) scoremu: Array1<f64>,
964    pub(crate) score_ls: Array1<f64>,
965    pub(crate) dscoremu: Array1<f64>,
966    pub(crate) dscore_ls: Array1<f64>,
967    pub(crate) hmumu: Array1<f64>,
968    pub(crate) hmu_ls: Array1<f64>,
969    pub(crate) h_ls_ls: Array1<f64>,
970    pub(crate) dhmumu: Array1<f64>,
971    pub(crate) dhmu_ls: Array1<f64>,
972    pub(crate) dh_ls_ls: Array1<f64>,
973}
974
975pub(crate) struct GaussianJointPsiSecondWeights {
976    pub(crate) objective_psi_psirow: Array1<f64>,
977    pub(crate) d2scoremu: Array1<f64>,
978    pub(crate) d2score_ls: Array1<f64>,
979    pub(crate) d2hmumu: Array1<f64>,
980    pub(crate) d2hmu_ls: Array1<f64>,
981    pub(crate) d2h_ls_ls: Array1<f64>,
982}
983
984pub(crate) struct GaussianJointPsiMixedDriftWeights {
985    pub(crate) dhmumu_u: Array1<f64>,
986    pub(crate) dhmu_ls_u: Array1<f64>,
987    pub(crate) dh_ls_ls_u: Array1<f64>,
988    pub(crate) d2hmumu: Array1<f64>,
989    pub(crate) d2hmu_ls: Array1<f64>,
990    pub(crate) d2h_ls_ls: Array1<f64>,
991}
992
993/// Apply a Horvitz–Thompson outer-row subsample mask to every per-row array
994/// of a `GaussianJointPsiFirstWeights` in place: each sampled row's
995/// contribution is multiplied by `WeightedOuterRow.weight = 1/π_i` and all
996/// non-sampled rows are zeroed. Every downstream assembly
997/// (`gaussian_joint_psi*_fromweights`,
998/// `build_two_block_custom_family_joint_psi_operator_from_actions`) consumes
999/// these arrays row-linearly via `Xᵀ diag(W) Y` and `weighted_crossprod_psi_maps`,
1000/// so the resulting first-order ψ score and Hessian remain unbiased estimators
1001/// of the full-data quantities.
1002pub(crate) fn apply_ht_mask_first(
1003    weights: &mut GaussianJointPsiFirstWeights,
1004    rows: &[crate::outer_subsample::WeightedOuterRow],
1005) {
1006    let n = weights.objective_psirow.len();
1007    let mut obj = Array1::<f64>::zeros(n);
1008    let mut smu = Array1::<f64>::zeros(n);
1009    let mut sls = Array1::<f64>::zeros(n);
1010    let mut dsmu = Array1::<f64>::zeros(n);
1011    let mut dsls = Array1::<f64>::zeros(n);
1012    let mut hmm = Array1::<f64>::zeros(n);
1013    let mut hml = Array1::<f64>::zeros(n);
1014    let mut hll = Array1::<f64>::zeros(n);
1015    let mut dhmm = Array1::<f64>::zeros(n);
1016    let mut dhml = Array1::<f64>::zeros(n);
1017    let mut dhll = Array1::<f64>::zeros(n);
1018    for r in rows {
1019        let i = r.index;
1020        let w = r.weight;
1021        obj[i] = weights.objective_psirow[i] * w;
1022        smu[i] = weights.scoremu[i] * w;
1023        sls[i] = weights.score_ls[i] * w;
1024        dsmu[i] = weights.dscoremu[i] * w;
1025        dsls[i] = weights.dscore_ls[i] * w;
1026        hmm[i] = weights.hmumu[i] * w;
1027        hml[i] = weights.hmu_ls[i] * w;
1028        hll[i] = weights.h_ls_ls[i] * w;
1029        dhmm[i] = weights.dhmumu[i] * w;
1030        dhml[i] = weights.dhmu_ls[i] * w;
1031        dhll[i] = weights.dh_ls_ls[i] * w;
1032    }
1033    weights.objective_psirow = obj;
1034    weights.scoremu = smu;
1035    weights.score_ls = sls;
1036    weights.dscoremu = dsmu;
1037    weights.dscore_ls = dsls;
1038    weights.hmumu = hmm;
1039    weights.hmu_ls = hml;
1040    weights.h_ls_ls = hll;
1041    weights.dhmumu = dhmm;
1042    weights.dhmu_ls = dhml;
1043    weights.dh_ls_ls = dhll;
1044}
1045
1046/// HT mask for `GaussianJointPsiSecondWeights`. Same semantics as
1047/// `apply_ht_mask_first`: each per-row contribution is scaled by 1/π_i and
1048/// non-sampled rows are zeroed. Consumed row-linearly by
1049/// `gaussian_joint_psisecondhessian_fromweights` and the `score_psi_psi`
1050/// `fast_atv(_, d2score_*)` reductions.
1051pub(crate) fn apply_ht_mask_second(
1052    weights: &mut GaussianJointPsiSecondWeights,
1053    rows: &[crate::outer_subsample::WeightedOuterRow],
1054) {
1055    let n = weights.objective_psi_psirow.len();
1056    let mut obj = Array1::<f64>::zeros(n);
1057    let mut d2smu = Array1::<f64>::zeros(n);
1058    let mut d2sls = Array1::<f64>::zeros(n);
1059    let mut d2hmm = Array1::<f64>::zeros(n);
1060    let mut d2hml = Array1::<f64>::zeros(n);
1061    let mut d2hll = Array1::<f64>::zeros(n);
1062    for r in rows {
1063        let i = r.index;
1064        let w = r.weight;
1065        obj[i] = weights.objective_psi_psirow[i] * w;
1066        d2smu[i] = weights.d2scoremu[i] * w;
1067        d2sls[i] = weights.d2score_ls[i] * w;
1068        d2hmm[i] = weights.d2hmumu[i] * w;
1069        d2hml[i] = weights.d2hmu_ls[i] * w;
1070        d2hll[i] = weights.d2h_ls_ls[i] * w;
1071    }
1072    weights.objective_psi_psirow = obj;
1073    weights.d2scoremu = d2smu;
1074    weights.d2score_ls = d2sls;
1075    weights.d2hmumu = d2hmm;
1076    weights.d2hmu_ls = d2hml;
1077    weights.d2h_ls_ls = d2hll;
1078}
1079
1080/// HT mask for `GaussianJointPsiMixedDriftWeights`. Same semantics as the
1081/// other `apply_ht_mask_*` helpers; consumed row-linearly by
1082/// `gaussian_joint_psi_mixedhessian_drift_fromweights`.
1083pub(crate) fn apply_ht_mask_mixed(
1084    weights: &mut GaussianJointPsiMixedDriftWeights,
1085    rows: &[crate::outer_subsample::WeightedOuterRow],
1086) {
1087    let n = weights.dhmumu_u.len();
1088    let mut dhmm_u = Array1::<f64>::zeros(n);
1089    let mut dhml_u = Array1::<f64>::zeros(n);
1090    let mut dhll_u = Array1::<f64>::zeros(n);
1091    let mut d2hmm = Array1::<f64>::zeros(n);
1092    let mut d2hml = Array1::<f64>::zeros(n);
1093    let mut d2hll = Array1::<f64>::zeros(n);
1094    for r in rows {
1095        let i = r.index;
1096        let w = r.weight;
1097        dhmm_u[i] = weights.dhmumu_u[i] * w;
1098        dhml_u[i] = weights.dhmu_ls_u[i] * w;
1099        dhll_u[i] = weights.dh_ls_ls_u[i] * w;
1100        d2hmm[i] = weights.d2hmumu[i] * w;
1101        d2hml[i] = weights.d2hmu_ls[i] * w;
1102        d2hll[i] = weights.d2h_ls_ls[i] * w;
1103    }
1104    weights.dhmumu_u = dhmm_u;
1105    weights.dhmu_ls_u = dhml_u;
1106    weights.dh_ls_ls_u = dhll_u;
1107    weights.d2hmumu = d2hmm;
1108    weights.d2hmu_ls = d2hml;
1109    weights.d2h_ls_ls = d2hll;
1110}
1111
1112pub(crate) fn gaussian_jointrow_scalars(
1113    y: &Array1<f64>,
1114    etamu: &Array1<f64>,
1115    eta_ls: &Array1<f64>,
1116    weights: &Array1<f64>,
1117) -> Result<GaussianJointRowScalars, String> {
1118    let nobs = y.len();
1119    if etamu.len() != nobs || eta_ls.len() != nobs || weights.len() != nobs {
1120        return Err(GamlssError::DimensionMismatch {
1121            reason: "Gaussian joint row scalar input size mismatch".to_string(),
1122        }
1123        .into());
1124    }
1125    let mut obs_weight = Array1::<f64>::uninit(nobs);
1126    let mut standardized_residual = Array1::<f64>::uninit(nobs);
1127    let mut inv_sigma = Array1::<f64>::uninit(nobs);
1128    let mut kappa = Array1::<f64>::uninit(nobs);
1129    let ln2pi = (2.0 * std::f64::consts::PI).ln();
1130    // Compute into an indexed temporary first. Parallel collection preserves
1131    // row order; scanning the results afterward reports the smallest failing
1132    // row deterministically and publishes no partially initialized scalar set.
1133    let certified: Vec<Result<GaussianDiagonalRowKernel, String>> = (0..nobs)
1134        .into_par_iter()
1135        .map(|i| gaussian_diagonal_row_kernel(i, y[i], etamu[i], eta_ls[i], weights[i], ln2pi))
1136        .collect();
1137    for (i, row) in certified.into_iter().enumerate() {
1138        let row = row?;
1139        obs_weight[i].write(weights[i]);
1140        standardized_residual[i].write(row.standardized_residual);
1141        inv_sigma[i].write(row.inv_sigma);
1142        kappa[i].write(row.kappa);
1143    }
1144    // SAFETY: every `MaybeUninit` slot in each of these arrays was written
1145    // exactly once in the `for i in 0..nobs` loop above; no slot is read,
1146    // moved, or dropped before this point.
1147    let (obs_weight, standardized_residual, inv_sigma, kappa) = unsafe {
1148        (
1149            obs_weight.assume_init(),
1150            standardized_residual.assume_init(),
1151            inv_sigma.assume_init(),
1152            kappa.assume_init(),
1153        )
1154    };
1155    Ok(GaussianJointRowScalars {
1156        obs_weight,
1157        standardized_residual,
1158        inv_sigma,
1159        kappa,
1160    })
1161}
1162
1163/// Live third-order observed-Hessian contraction emitted from the same stable
1164/// [`gaussian_normalized_row`] expression as the observed Hessian.
1165pub(crate) fn gaussian_joint_first_directionalweights(
1166    scalars: &GaussianJointRowScalars,
1167    dotmu: &Array1<f64>,
1168    dot_eta: &Array1<f64>,
1169) -> (Array1<f64>, Array1<f64>, Array1<f64>) {
1170    let nobs = scalars.obs_weight.len();
1171    let program = GaussianJointRowProgram::new(scalars);
1172    let mut w_u = Array1::<f64>::zeros(nobs);
1173    let mut c_u = Array1::<f64>::zeros(nobs);
1174    let mut d_u = Array1::<f64>::zeros(nobs);
1175    for i in 0..nobs {
1176        let matrix = program.row_third_contracted(i, &[dotmu[i], dot_eta[i]]);
1177        w_u[i] = matrix[0][0];
1178        c_u[i] = matrix[0][1];
1179        d_u[i] = matrix[1][1];
1180    }
1181    (w_u, c_u, d_u)
1182}
1183
1184/// Live fourth-order observed-Hessian contraction emitted from the same stable
1185/// [`gaussian_normalized_row`] expression as every lower curvature channel.
1186pub(crate) fn gaussian_jointsecond_directionalweights(
1187    scalars: &GaussianJointRowScalars,
1188    dotmu_u: &Array1<f64>,
1189    dot_eta_u: &Array1<f64>,
1190    dotmuv: &Array1<f64>,
1191    dot_etav: &Array1<f64>,
1192) -> (Array1<f64>, Array1<f64>, Array1<f64>) {
1193    let nobs = scalars.obs_weight.len();
1194    let program = GaussianJointRowProgram::new(scalars);
1195    let mut w_uv = Array1::<f64>::zeros(nobs);
1196    let mut c_uv = Array1::<f64>::zeros(nobs);
1197    let mut d_uv = Array1::<f64>::zeros(nobs);
1198    for i in 0..nobs {
1199        let matrix = program.row_fourth_contracted(
1200            i,
1201            &[dotmu_u[i], dot_eta_u[i]],
1202            &[dotmuv[i], dot_etav[i]],
1203        );
1204        w_uv[i] = matrix[0][0];
1205        c_uv[i] = matrix[0][1];
1206        d_uv[i] = matrix[1][1];
1207    }
1208    (w_uv, c_uv, d_uv)
1209}
1210
1211pub(crate) fn gaussian_joint_psi_firstweights(
1212    scalars: &GaussianJointRowScalars,
1213    mu_a: &Array1<f64>,
1214    eta_a: &Array1<f64>,
1215) -> GaussianJointPsiFirstWeights {
1216    let nobs = scalars.obs_weight.len();
1217    let program = GaussianJointRowProgram::new(scalars);
1218    let mut objective_psirow = Array1::<f64>::uninit(nobs);
1219    let mut scoremu = Array1::<f64>::uninit(nobs);
1220    let mut score_ls = Array1::<f64>::uninit(nobs);
1221    let mut dscoremu = Array1::<f64>::uninit(nobs);
1222    let mut dscore_ls = Array1::<f64>::uninit(nobs);
1223    let mut hmumu = Array1::<f64>::uninit(nobs);
1224    let mut hmu_ls = Array1::<f64>::uninit(nobs);
1225    let mut h_ls_ls = Array1::<f64>::uninit(nobs);
1226    let mut dhmumu = Array1::<f64>::uninit(nobs);
1227    let mut dhmu_ls = Array1::<f64>::uninit(nobs);
1228    let mut dh_ls_ls = Array1::<f64>::uninit(nobs);
1229    for i in 0..nobs {
1230        let direction = [mu_a[i], eta_a[i]];
1231        let atom = program.row_order2(i);
1232        let score = atom.gradient();
1233        let hessian = [
1234            [atom.hessian_at(0, 0), atom.hessian_at(0, 1)],
1235            [atom.hessian_at(1, 0), atom.hessian_at(1, 1)],
1236        ];
1237        let score_direction = matrix_vector_2(&hessian, &direction);
1238        let hessian_direction = program.row_third_contracted(i, &direction);
1239        objective_psirow[i].write(dot_2(&score, &direction));
1240        scoremu[i].write(score[0]);
1241        score_ls[i].write(score[1]);
1242        dscoremu[i].write(score_direction[0]);
1243        dscore_ls[i].write(score_direction[1]);
1244        hmumu[i].write(hessian[0][0]);
1245        hmu_ls[i].write(hessian[0][1]);
1246        h_ls_ls[i].write(hessian[1][1]);
1247        dhmumu[i].write(hessian_direction[0][0]);
1248        dhmu_ls[i].write(hessian_direction[0][1]);
1249        dh_ls_ls[i].write(hessian_direction[1][1]);
1250    }
1251    // SAFETY: every `MaybeUninit` slot in each field array was written
1252    // exactly once inside the `for i in 0..nobs` loop above.
1253    unsafe {
1254        GaussianJointPsiFirstWeights {
1255            objective_psirow: objective_psirow.assume_init(),
1256            scoremu: scoremu.assume_init(),
1257            score_ls: score_ls.assume_init(),
1258            dscoremu: dscoremu.assume_init(),
1259            dscore_ls: dscore_ls.assume_init(),
1260            hmumu: hmumu.assume_init(),
1261            hmu_ls: hmu_ls.assume_init(),
1262            h_ls_ls: h_ls_ls.assume_init(),
1263            dhmumu: dhmumu.assume_init(),
1264            dhmu_ls: dhmu_ls.assume_init(),
1265            dh_ls_ls: dh_ls_ls.assume_init(),
1266        }
1267    }
1268}
1269
1270pub(crate) fn gaussian_joint_psisecondweights(
1271    scalars: &GaussianJointRowScalars,
1272    mu_a: &Array1<f64>,
1273    eta_a: &Array1<f64>,
1274    mu_b: &Array1<f64>,
1275    eta_b: &Array1<f64>,
1276    mu_ab: &Array1<f64>,
1277    eta_ab: &Array1<f64>,
1278) -> GaussianJointPsiSecondWeights {
1279    let nobs = scalars.obs_weight.len();
1280    let program = GaussianJointRowProgram::new(scalars);
1281    let mut objective_psi_psirow = Array1::<f64>::uninit(nobs);
1282    let mut d2scoremu = Array1::<f64>::uninit(nobs);
1283    let mut d2score_ls = Array1::<f64>::uninit(nobs);
1284    let mut d2hmumu = Array1::<f64>::uninit(nobs);
1285    let mut d2hmu_ls = Array1::<f64>::uninit(nobs);
1286    let mut d2h_ls_ls = Array1::<f64>::uninit(nobs);
1287    for i in 0..nobs {
1288        let direction_a = [mu_a[i], eta_a[i]];
1289        let direction_b = [mu_b[i], eta_b[i]];
1290        let direction_ab = [mu_ab[i], eta_ab[i]];
1291        let atom = program.row_order2(i);
1292        let score = atom.gradient();
1293        let hessian = [
1294            [atom.hessian_at(0, 0), atom.hessian_at(0, 1)],
1295            [atom.hessian_at(1, 0), atom.hessian_at(1, 1)],
1296        ];
1297        let hessian_a = program.row_third_contracted(i, &direction_a);
1298        let hessian_ab = program.row_third_contracted(i, &direction_ab);
1299        let hessian_a_b = program.row_fourth_contracted(i, &direction_a, &direction_b);
1300        let score_second = add_vector_2(
1301            matrix_vector_2(&hessian_a, &direction_b),
1302            matrix_vector_2(&hessian, &direction_ab),
1303        );
1304        let hessian_second = add_matrix_2(hessian_a_b, hessian_ab);
1305        objective_psi_psirow[i].write(
1306            dot_2(&direction_a, &matrix_vector_2(&hessian, &direction_b))
1307                + dot_2(&score, &direction_ab),
1308        );
1309        d2scoremu[i].write(score_second[0]);
1310        d2score_ls[i].write(score_second[1]);
1311        d2hmumu[i].write(hessian_second[0][0]);
1312        d2hmu_ls[i].write(hessian_second[0][1]);
1313        d2h_ls_ls[i].write(hessian_second[1][1]);
1314    }
1315    // SAFETY: every `MaybeUninit` slot in each field array was written
1316    // exactly once inside the `for i in 0..nobs` loop above.
1317    unsafe {
1318        GaussianJointPsiSecondWeights {
1319            objective_psi_psirow: objective_psi_psirow.assume_init(),
1320            d2scoremu: d2scoremu.assume_init(),
1321            d2score_ls: d2score_ls.assume_init(),
1322            d2hmumu: d2hmumu.assume_init(),
1323            d2hmu_ls: d2hmu_ls.assume_init(),
1324            d2h_ls_ls: d2h_ls_ls.assume_init(),
1325        }
1326    }
1327}
1328
1329pub(crate) fn gaussian_joint_psi_mixed_driftweights(
1330    scalars: &GaussianJointRowScalars,
1331    dot_mu: &Array1<f64>,
1332    dot_eta: &Array1<f64>,
1333    mu_a: &Array1<f64>,
1334    eta_a: &Array1<f64>,
1335    dot_mu_a: &Array1<f64>,
1336    dot_eta_a: &Array1<f64>,
1337) -> GaussianJointPsiMixedDriftWeights {
1338    let nobs = scalars.obs_weight.len();
1339    let program = GaussianJointRowProgram::new(scalars);
1340    let mut dhmumu_u = Array1::<f64>::uninit(nobs);
1341    let mut dhmu_ls_u = Array1::<f64>::uninit(nobs);
1342    let mut dh_ls_ls_u = Array1::<f64>::uninit(nobs);
1343    let mut d2hmumu = Array1::<f64>::uninit(nobs);
1344    let mut d2hmu_ls = Array1::<f64>::uninit(nobs);
1345    let mut d2h_ls_ls = Array1::<f64>::uninit(nobs);
1346    for i in 0..nobs {
1347        let drift = [dot_mu[i], dot_eta[i]];
1348        let psi = [mu_a[i], eta_a[i]];
1349        let mixed_direction = [dot_mu_a[i], dot_eta_a[i]];
1350        let hessian_drift = program.row_third_contracted(i, &drift);
1351        let hessian_mixed = add_matrix_2(
1352            program.row_fourth_contracted(i, &drift, &psi),
1353            program.row_third_contracted(i, &mixed_direction),
1354        );
1355        dhmumu_u[i].write(hessian_drift[0][0]);
1356        dhmu_ls_u[i].write(hessian_drift[0][1]);
1357        dh_ls_ls_u[i].write(hessian_drift[1][1]);
1358        d2hmumu[i].write(hessian_mixed[0][0]);
1359        d2hmu_ls[i].write(hessian_mixed[0][1]);
1360        d2h_ls_ls[i].write(hessian_mixed[1][1]);
1361    }
1362    // SAFETY: every `MaybeUninit` slot in each field array was written
1363    // exactly once inside the `for i in 0..nobs` loop above.
1364    unsafe {
1365        GaussianJointPsiMixedDriftWeights {
1366            dhmumu_u: dhmumu_u.assume_init(),
1367            dhmu_ls_u: dhmu_ls_u.assume_init(),
1368            dh_ls_ls_u: dh_ls_ls_u.assume_init(),
1369            d2hmumu: d2hmumu.assume_init(),
1370            d2hmu_ls: d2hmu_ls.assume_init(),
1371            d2h_ls_ls: d2h_ls_ls.assume_init(),
1372        }
1373    }
1374}
1375
1376/// Canonical Gaussian location-scale OBSERVED joint-Hessian row coefficients
1377/// `(mm, ml, ll)` — the SINGLE source of truth for this curvature, shared by
1378/// every representation that assembles the value Hessian (the dense
1379/// `exact_newton_joint_hessian_from_designs` and the matrix-free
1380/// `GaussianLocationScaleHessianWorkspace`). Exact second derivatives of the
1381/// row NLL (`r = y−μ`, `w = a/σ²`, `m = rw`, `n = r²w`, `κ = dlogσ/dη`):
1382///   `mm = ∂²ℓ/∂η_μ²      = w`             (observed ≡ expected — exact),
1383///   `ml = ∂²ℓ/∂η_μ∂η_ls  = 2κm`           (expectation 0 at the truth),
1384///   `ll = ∂²ℓ/∂η_ls²     = κ′(a−n) + 2κ²n` (expectation 2κ²a).
1385/// The LAML criterion `−½log|H+S|` requires the OBSERVED penalized Hessian at
1386/// β̂ (Wood–Pya–Säfken 2016): the earlier block-Fisher object (#684/#566)
1387/// zeroed `ml` and expected `ll`, which drops the cross-block Schur deficit
1388/// `H_σμ(H_μμ+S_μ)⁻¹H_μσ` and the fitted-residual shrinkage `E[n̂]≈a(1−h_μ)`
1389/// — both overstate σ-block information and bias λ̂_σ upward on the flat scale
1390/// surface (#1561: log-σ over-smoothing; same dof genus as #2133). At a
1391/// true-null/flat σ surface `n→a`, `m→0`, so observed → Fisher and null
1392/// behavior is unchanged (SPEC: defaults recover the null). Indefiniteness of
1393/// the observed joint Hessian is handled by the existing #365 modified-Newton
1394/// reflection on the inner path and the spectral PD-floor on the criterion
1395/// log-det. Routing every path through this one constructor keeps the #684
1396/// cross-block drift structurally impossible.
1397pub(crate) fn gaussian_locscale_observed_joint_row_coeffs(
1398    rows: &GaussianJointRowScalars,
1399) -> (Array1<f64>, Array1<f64>, Array1<f64>) {
1400    let n = rows.obs_weight.len();
1401    let program = GaussianJointRowProgram::new(rows);
1402    let mut mm = Array1::<f64>::zeros(n);
1403    let mut ml = Array1::<f64>::zeros(n);
1404    let mut ll = Array1::<f64>::zeros(n);
1405    for row in 0..n {
1406        let atom = program.row_order2(row);
1407        mm[row] = atom.hessian_at(0, 0);
1408        ml[row] = atom.hessian_at(0, 1);
1409        ll[row] = atom.hessian_at(1, 1);
1410    }
1411    (mm, ml, ll)
1412}
1413
1414pub(crate) fn gaussian_joint_hessian_from_designs(
1415    xmu: &DenseOrOperator<'_>,
1416    x_ls: &DenseOrOperator<'_>,
1417    hmumu_coeff: &Array1<f64>,
1418    hmu_ls_coeff: &Array1<f64>,
1419    h_ls_ls_coeff: &Array1<f64>,
1420) -> Result<Array2<f64>, String> {
1421    if xmu.nrows() != hmumu_coeff.len()
1422        || xmu.nrows() != hmu_ls_coeff.len()
1423        || xmu.nrows() != h_ls_ls_coeff.len()
1424        || x_ls.nrows() != xmu.nrows()
1425    {
1426        return Err(GamlssError::DimensionMismatch { reason: format!(
1427            "gaussian_joint_hessian_from_designs dimension mismatch: xmu {}x{}, x_ls {}x{}, coeffs {}/{}/{}",
1428            xmu.nrows(),
1429            xmu.ncols(),
1430            x_ls.nrows(),
1431            x_ls.ncols(),
1432            hmumu_coeff.len(),
1433            hmu_ls_coeff.len(),
1434            h_ls_ls_coeff.len()
1435        ) }.into());
1436    }
1437
1438    let n = xmu.nrows();
1439    let pmu = xmu.ncols();
1440    let p_ls = x_ls.ncols();
1441    let total = pmu + p_ls;
1442    let mut out = Array2::<f64>::zeros((total, total));
1443    for rows in exact_design_row_chunks(n, pmu.max(p_ls)) {
1444        let xmu_chunk = xmu.row_chunk(rows.clone())?;
1445        let xls_chunk = x_ls.row_chunk(rows.clone())?;
1446        let hmumu = hmumu_coeff.slice(s![rows.clone()]);
1447        let hmu_ls = hmu_ls_coeff.slice(s![rows.clone()]);
1448        let h_ls_ls = h_ls_ls_coeff.slice(s![rows.clone()]);
1449        let chunk_hessian =
1450            fast_joint_hessian_2x2(&xmu_chunk, &xls_chunk, &hmumu, &hmu_ls, &h_ls_ls);
1451        out += &chunk_hessian;
1452    }
1453    Ok(out)
1454}
1455
1456pub(crate) fn gaussian_joint_psihessian_fromweights(
1457    xmu: &Array2<f64>,
1458    x_ls: &Array2<f64>,
1459    xmu_psi: CustomFamilyPsiLinearMapRef<'_>,
1460    x_ls_psi: CustomFamilyPsiLinearMapRef<'_>,
1461    weights: &GaussianJointPsiFirstWeights,
1462) -> Result<Array2<f64>, String> {
1463    // For the symmetric blocks (hmumu, h_ls_ls), the pair
1464    //   X_psi^T D X  and  X^T D X_psi
1465    // are transposes of each other, so compute one and add its transpose.
1466    let a_mu = weighted_crossprod_psi_maps(
1467        xmu_psi,
1468        weights.hmumu.view(),
1469        CustomFamilyPsiLinearMapRef::Dense(xmu),
1470    ).map_err(|error| error.to_string())?;
1471    let hmumu = &a_mu + &a_mu.t() + &xt_diag_x_dense(xmu, &weights.dhmumu)?;
1472    let hmu_ls = weighted_crossprod_psi_maps(
1473        xmu_psi,
1474        weights.hmu_ls.view(),
1475        CustomFamilyPsiLinearMapRef::Dense(x_ls),
1476    ).map_err(|error| error.to_string())? + &weighted_crossprod_psi_maps(
1477        CustomFamilyPsiLinearMapRef::Dense(xmu),
1478        weights.hmu_ls.view(),
1479        x_ls_psi,
1480    ).map_err(|error| error.to_string())? + &xt_diag_y_dense(xmu, &weights.dhmu_ls, x_ls)?;
1481    let a_ls = weighted_crossprod_psi_maps(
1482        x_ls_psi,
1483        weights.h_ls_ls.view(),
1484        CustomFamilyPsiLinearMapRef::Dense(x_ls),
1485    ).map_err(|error| error.to_string())?;
1486    let h_ls_ls = &a_ls + &a_ls.t() + &xt_diag_x_dense(x_ls, &weights.dh_ls_ls)?;
1487    Ok(gaussian_pack_joint_symmetrichessian(
1488        &hmumu, &hmu_ls, &h_ls_ls,
1489    ))
1490}
1491
1492pub(crate) fn build_two_block_custom_family_joint_psi_operator_from_actions(
1493    left_action: Option<CustomFamilyPsiDesignAction>,
1494    right_action: Option<CustomFamilyPsiDesignAction>,
1495    left_range: std::ops::Range<usize>,
1496    right_range: std::ops::Range<usize>,
1497    left_design: &Array2<f64>,
1498    right_design: &Array2<f64>,
1499    left_weights: &Array1<f64>,
1500    cross_weights: &Array1<f64>,
1501    right_weights: &Array1<f64>,
1502    left_drift_weights: &Array1<f64>,
1503    cross_drift_weights: &Array1<f64>,
1504    right_drift_weights: &Array1<f64>,
1505) -> Result<Option<std::sync::Arc<dyn gam_problem::HyperOperator>>, String> {
1506    if left_action.is_none() && right_action.is_none() {
1507        return Ok(None);
1508    }
1509
1510    let total = left_design.ncols() + right_design.ncols();
1511    let channels = vec![
1512        CustomFamilyJointDesignChannel::new(left_range, shared_dense_arc(left_design), left_action),
1513        CustomFamilyJointDesignChannel::new(
1514            right_range,
1515            shared_dense_arc(right_design),
1516            right_action,
1517        ),
1518    ];
1519    let pair_contributions = vec![
1520        CustomFamilyJointDesignPairContribution::new(
1521            0,
1522            0,
1523            left_weights.clone(),
1524            left_drift_weights.clone(),
1525        ),
1526        CustomFamilyJointDesignPairContribution::new(
1527            0,
1528            1,
1529            cross_weights.clone(),
1530            cross_drift_weights.clone(),
1531        ),
1532        CustomFamilyJointDesignPairContribution::new(
1533            1,
1534            0,
1535            cross_weights.clone(),
1536            cross_drift_weights.clone(),
1537        ),
1538        CustomFamilyJointDesignPairContribution::new(
1539            1,
1540            1,
1541            right_weights.clone(),
1542            right_drift_weights.clone(),
1543        ),
1544    ];
1545
1546    Ok(Some(std::sync::Arc::new(
1547        CustomFamilyJointPsiOperator::new(total, channels, pair_contributions),
1548    )))
1549}
1550
1551pub(crate) fn gaussian_joint_psisecondhessian_fromweights(
1552    xmu: &Array2<f64>,
1553    x_ls: &Array2<f64>,
1554    xmu_i: CustomFamilyPsiLinearMapRef<'_>,
1555    x_ls_i: CustomFamilyPsiLinearMapRef<'_>,
1556    xmu_j: CustomFamilyPsiLinearMapRef<'_>,
1557    x_ls_j: CustomFamilyPsiLinearMapRef<'_>,
1558    xmu_ab: CustomFamilyPsiLinearMapRef<'_>,
1559    x_ls_ab: CustomFamilyPsiLinearMapRef<'_>,
1560    weights_i: &GaussianJointPsiFirstWeights,
1561    weights_j: &GaussianJointPsiFirstWeights,
1562    secondweights: &GaussianJointPsiSecondWeights,
1563) -> Result<Array2<f64>, String> {
1564    // Exploit transpose symmetry: X_a^T D X_b and X_b^T D X_a are transposes.
1565    // For each such pair in the symmetric blocks (hmumu, h_ls_ls), compute one
1566    // and add its transpose, halving the number of O(np²) products.
1567    let a_ab_mu = weighted_crossprod_psi_maps(
1568        xmu_ab,
1569        weights_i.hmumu.view(),
1570        CustomFamilyPsiLinearMapRef::Dense(xmu),
1571    ).map_err(|error| error.to_string())?;
1572    let a_ij_mu = weighted_crossprod_psi_maps(xmu_i, weights_i.hmumu.view(), xmu_j).map_err(|error| error.to_string())?;
1573    let a_iwj_mu = weighted_crossprod_psi_maps(
1574        xmu_i,
1575        weights_j.dhmumu.view(),
1576        CustomFamilyPsiLinearMapRef::Dense(xmu),
1577    ).map_err(|error| error.to_string())?;
1578    let a_jwi_mu = weighted_crossprod_psi_maps(
1579        xmu_j,
1580        weights_i.dhmumu.view(),
1581        CustomFamilyPsiLinearMapRef::Dense(xmu),
1582    ).map_err(|error| error.to_string())?;
1583    let hmumu = &a_ab_mu
1584        + &a_ab_mu.t()
1585        + &a_ij_mu
1586        + a_ij_mu.t()
1587        + &a_iwj_mu
1588        + a_iwj_mu.t()
1589        + &a_jwi_mu
1590        + a_jwi_mu.t()
1591        + &xt_diag_x_dense(xmu, &secondweights.d2hmumu)?;
1592    let hmu_ls = weighted_crossprod_psi_maps(
1593        xmu_ab,
1594        weights_i.hmu_ls.view(),
1595        CustomFamilyPsiLinearMapRef::Dense(x_ls),
1596    ).map_err(|error| error.to_string())? + &weighted_crossprod_psi_maps(xmu_i, weights_i.hmu_ls.view(), x_ls_j).map_err(|error| error.to_string())?
1597        + &weighted_crossprod_psi_maps(xmu_j, weights_i.hmu_ls.view(), x_ls_i).map_err(|error| error.to_string())?
1598        + &weighted_crossprod_psi_maps(
1599            xmu_i,
1600            weights_j.dhmu_ls.view(),
1601            CustomFamilyPsiLinearMapRef::Dense(x_ls),
1602        ).map_err(|error| error.to_string())?
1603        + &weighted_crossprod_psi_maps(
1604            xmu_j,
1605            weights_i.dhmu_ls.view(),
1606            CustomFamilyPsiLinearMapRef::Dense(x_ls),
1607        ).map_err(|error| error.to_string())?
1608        + &weighted_crossprod_psi_maps(
1609            CustomFamilyPsiLinearMapRef::Dense(xmu),
1610            weights_i.dhmu_ls.view(),
1611            x_ls_j,
1612        ).map_err(|error| error.to_string())?
1613        + &weighted_crossprod_psi_maps(
1614            CustomFamilyPsiLinearMapRef::Dense(xmu),
1615            weights_j.dhmu_ls.view(),
1616            x_ls_i,
1617        ).map_err(|error| error.to_string())?
1618        + &xt_diag_y_dense(xmu, &secondweights.d2hmu_ls, x_ls)?
1619        + &weighted_crossprod_psi_maps(
1620            CustomFamilyPsiLinearMapRef::Dense(xmu),
1621            weights_i.hmu_ls.view(),
1622            x_ls_ab,
1623        ).map_err(|error| error.to_string())?;
1624    let a_ab_ls = weighted_crossprod_psi_maps(
1625        x_ls_ab,
1626        weights_i.h_ls_ls.view(),
1627        CustomFamilyPsiLinearMapRef::Dense(x_ls),
1628    ).map_err(|error| error.to_string())?;
1629    let a_ij_ls = weighted_crossprod_psi_maps(x_ls_i, weights_i.h_ls_ls.view(), x_ls_j).map_err(|error| error.to_string())?;
1630    let a_iwj_ls = weighted_crossprod_psi_maps(
1631        x_ls_i,
1632        weights_j.dh_ls_ls.view(),
1633        CustomFamilyPsiLinearMapRef::Dense(x_ls),
1634    ).map_err(|error| error.to_string())?;
1635    let a_jwi_ls = weighted_crossprod_psi_maps(
1636        x_ls_j,
1637        weights_i.dh_ls_ls.view(),
1638        CustomFamilyPsiLinearMapRef::Dense(x_ls),
1639    ).map_err(|error| error.to_string())?;
1640    let h_ls_ls = &a_ab_ls
1641        + &a_ab_ls.t()
1642        + &a_ij_ls
1643        + a_ij_ls.t()
1644        + &a_iwj_ls
1645        + a_iwj_ls.t()
1646        + &a_jwi_ls
1647        + a_jwi_ls.t()
1648        + &xt_diag_x_dense(x_ls, &secondweights.d2h_ls_ls)?;
1649    Ok(gaussian_pack_joint_symmetrichessian(
1650        &hmumu, &hmu_ls, &h_ls_ls,
1651    ))
1652}
1653
1654pub(crate) fn gaussian_joint_psi_mixedhessian_drift_fromweights(
1655    xmu: &Array2<f64>,
1656    x_ls: &Array2<f64>,
1657    xmu_psi: CustomFamilyPsiLinearMapRef<'_>,
1658    x_ls_psi: CustomFamilyPsiLinearMapRef<'_>,
1659    mixedweights: &GaussianJointPsiMixedDriftWeights,
1660) -> Result<Array2<f64>, String> {
1661    let a_mu = weighted_crossprod_psi_maps(
1662        xmu_psi,
1663        mixedweights.dhmumu_u.view(),
1664        CustomFamilyPsiLinearMapRef::Dense(xmu),
1665    ).map_err(|error| error.to_string())?;
1666    let hmumu = &a_mu + &a_mu.t() + &xt_diag_x_dense(xmu, &mixedweights.d2hmumu)?;
1667    let hmu_ls = weighted_crossprod_psi_maps(
1668        xmu_psi,
1669        mixedweights.dhmu_ls_u.view(),
1670        CustomFamilyPsiLinearMapRef::Dense(x_ls),
1671    ).map_err(|error| error.to_string())? + &weighted_crossprod_psi_maps(
1672        CustomFamilyPsiLinearMapRef::Dense(xmu),
1673        mixedweights.dhmu_ls_u.view(),
1674        x_ls_psi,
1675    ).map_err(|error| error.to_string())? + &xt_diag_y_dense(xmu, &mixedweights.d2hmu_ls, x_ls)?;
1676    let a_ls = weighted_crossprod_psi_maps(
1677        x_ls_psi,
1678        mixedweights.dh_ls_ls_u.view(),
1679        CustomFamilyPsiLinearMapRef::Dense(x_ls),
1680    ).map_err(|error| error.to_string())?;
1681    let h_ls_ls = &a_ls + &a_ls.t() + &xt_diag_x_dense(x_ls, &mixedweights.d2h_ls_ls)?;
1682    Ok(gaussian_pack_joint_symmetrichessian(
1683        &hmumu, &hmu_ls, &h_ls_ls,
1684    ))
1685}
1686
1687#[inline]
1688pub(crate) fn exp_sigma_derivs_up_to_fourth_array(
1689    eta: ArrayView1<'_, f64>,
1690) -> (
1691    Array1<f64>,
1692    Array1<f64>,
1693    Array1<f64>,
1694    Array1<f64>,
1695    Array1<f64>,
1696) {
1697    use rayon::iter::{IntoParallelIterator, ParallelIterator};
1698    let n = eta.len();
1699    let tuples: Vec<(f64, f64, f64, f64, f64)> = (0..n)
1700        .into_par_iter()
1701        .map(|i| exp_sigma_derivs_up_to_fourth_scalar(eta[i]))
1702        .collect();
1703    let mut sigma = Array1::<f64>::zeros(n);
1704    let mut d1 = Array1::<f64>::zeros(n);
1705    let mut d2 = Array1::<f64>::zeros(n);
1706    let mut d3 = Array1::<f64>::zeros(n);
1707    let mut d4 = Array1::<f64>::zeros(n);
1708    for (i, (s_i, d1_i, d2_i, d3_i, d4_i)) in tuples.into_iter().enumerate() {
1709        sigma[i] = s_i;
1710        d1[i] = d1_i;
1711        d2[i] = d2_i;
1712        d3[i] = d3_i;
1713        d4[i] = d4_i;
1714    }
1715    (sigma, d1, d2, d3, d4)
1716}
1717
1718#[cfg(test)]
1719mod observed_single_source_oracle_tests {
1720    //! #932 doctrine oracle for the generated Gaussian location-scale row
1721    //! program and its OBSERVED joint-Hessian tower.
1722    //!
1723    //! Every non-wiggle live score/Hessian/ψ channel is a chain-rule projection
1724    //! of the symbolic order2/third/fourth lowerings emitted from
1725    //! `gaussian_normalized_row`. The generic nested-jet evaluator and the
1726    //! likelihood-only finite differences below are independent witnesses.
1727    //!
1728    //! MECHANICAL SOURCE (no hand math reused):
1729    //!  * The per-row negative log-likelihood is `ρ(μ,η)=−ℓ(μ,η)`, evaluated by
1730    //!    the production row kernel `gaussian_diagonal_row_kernel`.
1731    //!  * Its OBSERVED 2×2 Hessian in `(μ,η_ls)` and every directional chain are
1732    //!    taken by central finite differences of that likelihood alone.
1733
1734    use super::*;
1735    use ndarray::array;
1736
1737    /// Row negative log-likelihood from the production kernel (likelihood only,
1738    /// no curvature coefficients involved).
1739    fn row_nll(y: f64, mu: f64, eta_ls: f64, a: f64) -> f64 {
1740        let ln2pi = (2.0 * std::f64::consts::PI).ln();
1741        -gaussian_diagonal_row_kernel(0, y, mu, eta_ls, a, ln2pi)
1742            .expect("representable Gaussian oracle row")
1743            .log_likelihood
1744    }
1745
1746    fn gradient_fd(y: f64, mu: f64, eta_ls: f64, a: f64, h: f64) -> [f64; 2] {
1747        [
1748            (row_nll(y, mu + h, eta_ls, a) - row_nll(y, mu - h, eta_ls, a)) / (2.0 * h),
1749            (row_nll(y, mu, eta_ls + h, a) - row_nll(y, mu, eta_ls - h, a)) / (2.0 * h),
1750        ]
1751    }
1752
1753    /// Observed 2×2 Hessian of the row NLL in `(μ, η_ls)` by central FD.
1754    /// Returns `(H_μμ, H_{μ,ls}, H_{ls,ls})`.
1755    fn observed_hessian_fd(y: f64, mu: f64, eta_ls: f64, a: f64, h: f64) -> (f64, f64, f64) {
1756        let hmm = (row_nll(y, mu + h, eta_ls, a) - 2.0 * row_nll(y, mu, eta_ls, a)
1757            + row_nll(y, mu - h, eta_ls, a))
1758            / (h * h);
1759        let hll = (row_nll(y, mu, eta_ls + h, a) - 2.0 * row_nll(y, mu, eta_ls, a)
1760            + row_nll(y, mu, eta_ls - h, a))
1761            / (h * h);
1762        let hml = (row_nll(y, mu + h, eta_ls + h, a)
1763            - row_nll(y, mu + h, eta_ls - h, a)
1764            - row_nll(y, mu - h, eta_ls + h, a)
1765            + row_nll(y, mu - h, eta_ls - h, a))
1766            / (4.0 * h * h);
1767        (hmm, hml, hll)
1768    }
1769
1770    fn hessian_fd(y: f64, mu: f64, eta_ls: f64, a: f64, h: f64) -> [[f64; 2]; 2] {
1771        let (mm, ml, ll) = observed_hessian_fd(y, mu, eta_ls, a, h);
1772        [[mm, ml], [ml, ll]]
1773    }
1774
1775    fn path_point(
1776        base: [f64; 2],
1777        direction_a: [f64; 2],
1778        direction_b: [f64; 2],
1779        direction_ab: [f64; 2],
1780        s: f64,
1781        t: f64,
1782    ) -> [f64; 2] {
1783        [
1784            base[0] + s * direction_a[0] + t * direction_b[0] + s * t * direction_ab[0],
1785            base[1] + s * direction_a[1] + t * direction_b[1] + s * t * direction_ab[1],
1786        ]
1787    }
1788
1789    fn mixed_value_fd(
1790        y: f64,
1791        base: [f64; 2],
1792        a: f64,
1793        direction_a: [f64; 2],
1794        direction_b: [f64; 2],
1795        direction_ab: [f64; 2],
1796        h: f64,
1797    ) -> f64 {
1798        let pp = path_point(base, direction_a, direction_b, direction_ab, h, h);
1799        let pm = path_point(base, direction_a, direction_b, direction_ab, h, -h);
1800        let mp = path_point(base, direction_a, direction_b, direction_ab, -h, h);
1801        let mm = path_point(base, direction_a, direction_b, direction_ab, -h, -h);
1802        (row_nll(y, pp[0], pp[1], a) - row_nll(y, pm[0], pm[1], a) - row_nll(y, mp[0], mp[1], a)
1803            + row_nll(y, mm[0], mm[1], a))
1804            / (4.0 * h * h)
1805    }
1806
1807    fn mixed_gradient_fd(
1808        y: f64,
1809        base: [f64; 2],
1810        a: f64,
1811        direction_a: [f64; 2],
1812        direction_b: [f64; 2],
1813        direction_ab: [f64; 2],
1814        outer_h: f64,
1815        inner_h: f64,
1816    ) -> [f64; 2] {
1817        let pp = path_point(
1818            base,
1819            direction_a,
1820            direction_b,
1821            direction_ab,
1822            outer_h,
1823            outer_h,
1824        );
1825        let pm = path_point(
1826            base,
1827            direction_a,
1828            direction_b,
1829            direction_ab,
1830            outer_h,
1831            -outer_h,
1832        );
1833        let mp = path_point(
1834            base,
1835            direction_a,
1836            direction_b,
1837            direction_ab,
1838            -outer_h,
1839            outer_h,
1840        );
1841        let mm = path_point(
1842            base,
1843            direction_a,
1844            direction_b,
1845            direction_ab,
1846            -outer_h,
1847            -outer_h,
1848        );
1849        let gpp = gradient_fd(y, pp[0], pp[1], a, inner_h);
1850        let gpm = gradient_fd(y, pm[0], pm[1], a, inner_h);
1851        let gmp = gradient_fd(y, mp[0], mp[1], a, inner_h);
1852        let gmm = gradient_fd(y, mm[0], mm[1], a, inner_h);
1853        std::array::from_fn(|axis| {
1854            (gpp[axis] - gpm[axis] - gmp[axis] + gmm[axis]) / (4.0 * outer_h * outer_h)
1855        })
1856    }
1857
1858    fn mixed_hessian_fd(
1859        y: f64,
1860        base: [f64; 2],
1861        a: f64,
1862        direction_a: [f64; 2],
1863        direction_b: [f64; 2],
1864        direction_ab: [f64; 2],
1865        outer_h: f64,
1866        inner_h: f64,
1867    ) -> [[f64; 2]; 2] {
1868        let pp = path_point(
1869            base,
1870            direction_a,
1871            direction_b,
1872            direction_ab,
1873            outer_h,
1874            outer_h,
1875        );
1876        let pm = path_point(
1877            base,
1878            direction_a,
1879            direction_b,
1880            direction_ab,
1881            outer_h,
1882            -outer_h,
1883        );
1884        let mp = path_point(
1885            base,
1886            direction_a,
1887            direction_b,
1888            direction_ab,
1889            -outer_h,
1890            outer_h,
1891        );
1892        let mm = path_point(
1893            base,
1894            direction_a,
1895            direction_b,
1896            direction_ab,
1897            -outer_h,
1898            -outer_h,
1899        );
1900        let hpp = hessian_fd(y, pp[0], pp[1], a, inner_h);
1901        let hpm = hessian_fd(y, pm[0], pm[1], a, inner_h);
1902        let hmp = hessian_fd(y, mp[0], mp[1], a, inner_h);
1903        let hmm = hessian_fd(y, mm[0], mm[1], a, inner_h);
1904        std::array::from_fn(|row| {
1905            std::array::from_fn(|column| {
1906                (hpp[row][column] - hpm[row][column] - hmp[row][column] + hmm[row][column])
1907                    / (4.0 * outer_h * outer_h)
1908            })
1909        })
1910    }
1911
1912    fn assert_close(actual: f64, expected: f64, tolerance: f64, label: &str) {
1913        let band = tolerance * actual.abs().max(expected.abs()).max(1.0);
1914        assert!(
1915            (actual - expected).abs() <= band,
1916            "{label}: actual={actual:+.15e} expected={expected:+.15e} band={band:.3e}"
1917        );
1918    }
1919
1920    fn assert_matrix_close(
1921        actual: &[[f64; 2]; 2],
1922        expected: &[[f64; 2]; 2],
1923        tolerance: f64,
1924        label: &str,
1925    ) {
1926        for row in 0..2 {
1927            for column in 0..2 {
1928                assert_close(
1929                    actual[row][column],
1930                    expected[row][column],
1931                    tolerance,
1932                    &format!("{label}[{row},{column}]"),
1933                );
1934            }
1935        }
1936    }
1937
1938    /// Production observed joint-Hessian coefficients for a single row.
1939    fn production_observed_row(y: f64, mu: f64, eta_ls: f64, a: f64) -> (f64, f64, f64) {
1940        let rows = gaussian_jointrow_scalars(&array![y], &array![mu], &array![eta_ls], &array![a])
1941            .expect("row scalars");
1942        let (mm, ml, ll) = gaussian_locscale_observed_joint_row_coeffs(&rows);
1943        (mm[0], ml[0], ll[0])
1944    }
1945
1946    #[test]
1947    fn generated_gaussian_psi_chain_matches_generic_nested_jet_all_channels_932() {
1948        use gam_math::jet_tower::{
1949            program_fourth_contracted, program_row_kernel, program_third_contracted,
1950        };
1951
1952        let rows =
1953            gaussian_jointrow_scalars(&array![0.55], &array![0.3], &array![-0.4], &array![1.7])
1954                .expect("row scalars");
1955        let program = GaussianJointRowProgram::new(&rows);
1956        let direction_a = [0.5, -0.7];
1957        let direction_b = [-0.3, 0.8];
1958        let direction_ab = [0.2, -0.15];
1959        let drift = [0.6, 0.25];
1960        let psi = [-0.4, 0.75];
1961        let drift_psi = [0.12, -0.18];
1962
1963        let atom = program.row_order2(0);
1964        let (jet_value, jet_score, jet_hessian) =
1965            program_row_kernel(&program, 0).expect("generic order2");
1966        assert_close(atom.value(), jet_value, 1e-12, "row value");
1967        for axis in 0..2 {
1968            assert_close(
1969                atom.gradient()[axis],
1970                jet_score[axis],
1971                1e-12,
1972                &format!("row score[{axis}]"),
1973            );
1974        }
1975        let generated_hessian = [
1976            [atom.hessian_at(0, 0), atom.hessian_at(0, 1)],
1977            [atom.hessian_at(1, 0), atom.hessian_at(1, 1)],
1978        ];
1979        assert_matrix_close(&generated_hessian, &jet_hessian, 1e-12, "row Hessian");
1980
1981        let jet_third_a =
1982            program_third_contracted(&program, 0, &direction_a).expect("generic third a");
1983        let jet_third_ab =
1984            program_third_contracted(&program, 0, &direction_ab).expect("generic third ab");
1985        let jet_fourth_ab = program_fourth_contracted(&program, 0, &direction_a, &direction_b)
1986            .expect("generic fourth ab");
1987        assert_matrix_close(
1988            &program.row_third_contracted(0, &direction_a),
1989            &jet_third_a,
1990            1e-9,
1991            "generated dH",
1992        );
1993        assert_matrix_close(
1994            &program.row_fourth_contracted(0, &direction_a, &direction_b),
1995            &jet_fourth_ab,
1996            1e-9,
1997            "generated d2H",
1998        );
1999
2000        let first = gaussian_joint_psi_firstweights(
2001            &rows,
2002            &array![direction_a[0]],
2003            &array![direction_a[1]],
2004        );
2005        let expected_score_a = matrix_vector_2(&jet_hessian, &direction_a);
2006        assert_close(
2007            first.objective_psirow[0],
2008            dot_2(&jet_score, &direction_a),
2009            1e-9,
2010            "first psi objective",
2011        );
2012        assert_close(first.scoremu[0], jet_score[0], 1e-12, "first score mu");
2013        assert_close(first.score_ls[0], jet_score[1], 1e-12, "first score ls");
2014        assert_close(
2015            first.dscoremu[0],
2016            expected_score_a[0],
2017            1e-9,
2018            "first dscore mu",
2019        );
2020        assert_close(
2021            first.dscore_ls[0],
2022            expected_score_a[1],
2023            1e-9,
2024            "first dscore ls",
2025        );
2026        assert_close(first.hmumu[0], jet_hessian[0][0], 1e-12, "first H mm");
2027        assert_close(first.hmu_ls[0], jet_hessian[0][1], 1e-12, "first H ml");
2028        assert_close(first.h_ls_ls[0], jet_hessian[1][1], 1e-12, "first H ll");
2029        assert_close(first.dhmumu[0], jet_third_a[0][0], 1e-9, "first dH mm");
2030        assert_close(first.dhmu_ls[0], jet_third_a[0][1], 1e-9, "first dH ml");
2031        assert_close(first.dh_ls_ls[0], jet_third_a[1][1], 1e-9, "first dH ll");
2032
2033        let second = gaussian_joint_psisecondweights(
2034            &rows,
2035            &array![direction_a[0]],
2036            &array![direction_a[1]],
2037            &array![direction_b[0]],
2038            &array![direction_b[1]],
2039            &array![direction_ab[0]],
2040            &array![direction_ab[1]],
2041        );
2042        let expected_second_score = add_vector_2(
2043            matrix_vector_2(&jet_third_a, &direction_b),
2044            matrix_vector_2(&jet_hessian, &direction_ab),
2045        );
2046        let expected_second_hessian = add_matrix_2(jet_fourth_ab, jet_third_ab);
2047        assert_close(
2048            second.objective_psi_psirow[0],
2049            dot_2(&direction_a, &matrix_vector_2(&jet_hessian, &direction_b))
2050                + dot_2(&jet_score, &direction_ab),
2051            1e-9,
2052            "second psi objective",
2053        );
2054        assert_close(
2055            second.d2scoremu[0],
2056            expected_second_score[0],
2057            1e-9,
2058            "second psi score mu",
2059        );
2060        assert_close(
2061            second.d2score_ls[0],
2062            expected_second_score[1],
2063            1e-9,
2064            "second psi score ls",
2065        );
2066        assert_close(
2067            second.d2hmumu[0],
2068            expected_second_hessian[0][0],
2069            1e-9,
2070            "second psi H mm",
2071        );
2072        assert_close(
2073            second.d2hmu_ls[0],
2074            expected_second_hessian[0][1],
2075            1e-9,
2076            "second psi H ml",
2077        );
2078        assert_close(
2079            second.d2h_ls_ls[0],
2080            expected_second_hessian[1][1],
2081            1e-9,
2082            "second psi H ll",
2083        );
2084
2085        let mixed = gaussian_joint_psi_mixed_driftweights(
2086            &rows,
2087            &array![drift[0]],
2088            &array![drift[1]],
2089            &array![psi[0]],
2090            &array![psi[1]],
2091            &array![drift_psi[0]],
2092            &array![drift_psi[1]],
2093        );
2094        let jet_third_drift =
2095            program_third_contracted(&program, 0, &drift).expect("generic third drift");
2096        let jet_mixed_hessian = add_matrix_2(
2097            program_fourth_contracted(&program, 0, &drift, &psi).expect("generic fourth mixed"),
2098            program_third_contracted(&program, 0, &drift_psi)
2099                .expect("generic third mixed direction"),
2100        );
2101        assert_close(
2102            mixed.dhmumu_u[0],
2103            jet_third_drift[0][0],
2104            1e-9,
2105            "mixed dH mm",
2106        );
2107        assert_close(
2108            mixed.dhmu_ls_u[0],
2109            jet_third_drift[0][1],
2110            1e-9,
2111            "mixed dH ml",
2112        );
2113        assert_close(
2114            mixed.dh_ls_ls_u[0],
2115            jet_third_drift[1][1],
2116            1e-9,
2117            "mixed dH ll",
2118        );
2119        assert_close(
2120            mixed.d2hmumu[0],
2121            jet_mixed_hessian[0][0],
2122            1e-9,
2123            "mixed d2H mm",
2124        );
2125        assert_close(
2126            mixed.d2hmu_ls[0],
2127            jet_mixed_hessian[0][1],
2128            1e-9,
2129            "mixed d2H ml",
2130        );
2131        assert_close(
2132            mixed.d2h_ls_ls[0],
2133            jet_mixed_hessian[1][1],
2134            1e-9,
2135            "mixed d2H ll",
2136        );
2137    }
2138
2139    #[test]
2140    fn generated_gaussian_psi_chain_matches_likelihood_finite_differences_932() {
2141        let y = 0.55;
2142        let base = [0.3, -0.4];
2143        let weight = 1.7;
2144        let direction_a = [0.5, -0.7];
2145        let direction_b = [-0.3, 0.8];
2146        let direction_ab = [0.2, -0.15];
2147        let drift = [0.6, 0.25];
2148        let psi = [-0.4, 0.75];
2149        let drift_psi = [0.12, -0.18];
2150        let rows = gaussian_jointrow_scalars(
2151            &array![y],
2152            &array![base[0]],
2153            &array![base[1]],
2154            &array![weight],
2155        )
2156        .expect("row scalars");
2157        let program = GaussianJointRowProgram::new(&rows);
2158        let atom = program.row_order2(0);
2159
2160        let normalized_value =
2161            0.5 * weight * rows.standardized_residual[0] * rows.standardized_residual[0];
2162        assert_close(
2163            atom.value(),
2164            normalized_value,
2165            1e-12,
2166            "normalized row value",
2167        );
2168        let score_fd = gradient_fd(y, base[0], base[1], weight, 2e-5);
2169        let hessian_fd0 = hessian_fd(y, base[0], base[1], weight, 1e-3);
2170        for axis in 0..2 {
2171            assert_close(
2172                atom.gradient()[axis],
2173                score_fd[axis],
2174                2e-8,
2175                &format!("score FD {axis}"),
2176            );
2177        }
2178        let atom_hessian = [
2179            [atom.hessian_at(0, 0), atom.hessian_at(0, 1)],
2180            [atom.hessian_at(1, 0), atom.hessian_at(1, 1)],
2181        ];
2182        assert_matrix_close(&atom_hessian, &hessian_fd0, 3e-6, "Hessian FD");
2183
2184        let first = gaussian_joint_psi_firstweights(
2185            &rows,
2186            &array![direction_a[0]],
2187            &array![direction_a[1]],
2188        );
2189        let first_step = 1e-2;
2190        let plus = [
2191            base[0] + first_step * direction_a[0],
2192            base[1] + first_step * direction_a[1],
2193        ];
2194        let minus = [
2195            base[0] - first_step * direction_a[0],
2196            base[1] - first_step * direction_a[1],
2197        ];
2198        let objective_first_fd = (row_nll(y, plus[0], plus[1], weight)
2199            - row_nll(y, minus[0], minus[1], weight))
2200            / (2.0 * first_step);
2201        let gradient_plus = gradient_fd(y, plus[0], plus[1], weight, 2e-5);
2202        let gradient_minus = gradient_fd(y, minus[0], minus[1], weight, 2e-5);
2203        let score_first_fd: [f64; 2] = std::array::from_fn(|axis| {
2204            (gradient_plus[axis] - gradient_minus[axis]) / (2.0 * first_step)
2205        });
2206        let hessian_plus = hessian_fd(y, plus[0], plus[1], weight, 1e-3);
2207        let hessian_minus = hessian_fd(y, minus[0], minus[1], weight, 1e-3);
2208        let hessian_first_fd: [[f64; 2]; 2] = std::array::from_fn(|row| {
2209            std::array::from_fn(|column| {
2210                (hessian_plus[row][column] - hessian_minus[row][column]) / (2.0 * first_step)
2211            })
2212        });
2213        assert_close(
2214            first.objective_psirow[0],
2215            objective_first_fd,
2216            2e-4,
2217            "first objective FD",
2218        );
2219        assert_close(
2220            first.dscoremu[0],
2221            score_first_fd[0],
2222            2e-4,
2223            "first score mu FD",
2224        );
2225        assert_close(
2226            first.dscore_ls[0],
2227            score_first_fd[1],
2228            2e-4,
2229            "first score ls FD",
2230        );
2231        assert_close(
2232            first.dhmumu[0],
2233            hessian_first_fd[0][0],
2234            2e-3,
2235            "first H mm FD",
2236        );
2237        assert_close(
2238            first.dhmu_ls[0],
2239            hessian_first_fd[0][1],
2240            2e-3,
2241            "first H ml FD",
2242        );
2243        assert_close(
2244            first.dh_ls_ls[0],
2245            hessian_first_fd[1][1],
2246            2e-3,
2247            "first H ll FD",
2248        );
2249
2250        let second = gaussian_joint_psisecondweights(
2251            &rows,
2252            &array![direction_a[0]],
2253            &array![direction_a[1]],
2254            &array![direction_b[0]],
2255            &array![direction_b[1]],
2256            &array![direction_ab[0]],
2257            &array![direction_ab[1]],
2258        );
2259        let objective_second_fd = mixed_value_fd(
2260            y,
2261            base,
2262            weight,
2263            direction_a,
2264            direction_b,
2265            direction_ab,
2266            5e-3,
2267        );
2268        let score_second_fd = mixed_gradient_fd(
2269            y,
2270            base,
2271            weight,
2272            direction_a,
2273            direction_b,
2274            direction_ab,
2275            1e-2,
2276            2e-5,
2277        );
2278        let hessian_second_fd = mixed_hessian_fd(
2279            y,
2280            base,
2281            weight,
2282            direction_a,
2283            direction_b,
2284            direction_ab,
2285            2e-2,
2286            1e-3,
2287        );
2288        assert_close(
2289            second.objective_psi_psirow[0],
2290            objective_second_fd,
2291            5e-4,
2292            "second objective FD",
2293        );
2294        assert_close(
2295            second.d2scoremu[0],
2296            score_second_fd[0],
2297            2e-3,
2298            "second score mu FD",
2299        );
2300        assert_close(
2301            second.d2score_ls[0],
2302            score_second_fd[1],
2303            2e-3,
2304            "second score ls FD",
2305        );
2306        assert_close(
2307            second.d2hmumu[0],
2308            hessian_second_fd[0][0],
2309            3e-2,
2310            "second H mm FD",
2311        );
2312        assert_close(
2313            second.d2hmu_ls[0],
2314            hessian_second_fd[0][1],
2315            3e-2,
2316            "second H ml FD",
2317        );
2318        assert_close(
2319            second.d2h_ls_ls[0],
2320            hessian_second_fd[1][1],
2321            3e-2,
2322            "second H ll FD",
2323        );
2324
2325        let mixed = gaussian_joint_psi_mixed_driftweights(
2326            &rows,
2327            &array![drift[0]],
2328            &array![drift[1]],
2329            &array![psi[0]],
2330            &array![psi[1]],
2331            &array![drift_psi[0]],
2332            &array![drift_psi[1]],
2333        );
2334        let drift_plus = [
2335            base[0] + first_step * drift[0],
2336            base[1] + first_step * drift[1],
2337        ];
2338        let drift_minus = [
2339            base[0] - first_step * drift[0],
2340            base[1] - first_step * drift[1],
2341        ];
2342        let drift_hessian_plus = hessian_fd(y, drift_plus[0], drift_plus[1], weight, 1e-3);
2343        let drift_hessian_minus = hessian_fd(y, drift_minus[0], drift_minus[1], weight, 1e-3);
2344        let drift_hessian_fd: [[f64; 2]; 2] = std::array::from_fn(|row| {
2345            std::array::from_fn(|column| {
2346                (drift_hessian_plus[row][column] - drift_hessian_minus[row][column])
2347                    / (2.0 * first_step)
2348            })
2349        });
2350        let mixed_hessian = mixed_hessian_fd(y, base, weight, drift, psi, drift_psi, 2e-2, 1e-3);
2351        assert_close(
2352            mixed.dhmumu_u[0],
2353            drift_hessian_fd[0][0],
2354            2e-3,
2355            "mixed dH mm FD",
2356        );
2357        assert_close(
2358            mixed.dhmu_ls_u[0],
2359            drift_hessian_fd[0][1],
2360            2e-3,
2361            "mixed dH ml FD",
2362        );
2363        assert_close(
2364            mixed.dh_ls_ls_u[0],
2365            drift_hessian_fd[1][1],
2366            2e-3,
2367            "mixed dH ll FD",
2368        );
2369        assert_close(
2370            mixed.d2hmumu[0],
2371            mixed_hessian[0][0],
2372            3e-2,
2373            "mixed d2H mm FD",
2374        );
2375        assert_close(
2376            mixed.d2hmu_ls[0],
2377            mixed_hessian[0][1],
2378            3e-2,
2379            "mixed d2H ml FD",
2380        );
2381        assert_close(
2382            mixed.d2h_ls_ls[0],
2383            mixed_hessian[1][1],
2384            3e-2,
2385            "mixed d2H ll FD",
2386        );
2387    }
2388
2389    #[test]
2390    fn observed_joint_row_coeffs_match_likelihood_fd_single_source() {
2391        // Residual-dependent cases: y ≠ μ so the cross and (ls,ls) observed
2392        // weights are material (not the Fisher limit m→0, n→a).
2393        let cases = [
2394            (0.3_f64, -0.4_f64, 1.0_f64, 0.55_f64),
2395            (-1.2, 0.7, 2.5, -0.9),
2396            (0.0, 1.5, 0.4, 0.35),
2397            (2.4, -1.1, 0.8, 1.7),
2398            (-0.6, 0.2, 3.3, -1.1),
2399        ];
2400        let h = 1e-4;
2401        for &(mu, eta_ls, a, y) in &cases {
2402            let (mm_hand, ml_hand, ll_hand) = production_observed_row(y, mu, eta_ls, a);
2403            let (mm_fd, ml_fd, ll_fd) = observed_hessian_fd(y, mu, eta_ls, a, h);
2404            assert!(
2405                (mm_hand - mm_fd).abs() <= 1e-5 * mm_hand.abs().max(1.0),
2406                "H_μμ observed μ={mu} η={eta_ls} y={y}: hand={mm_hand} fd={mm_fd}"
2407            );
2408            assert!(
2409                (ml_hand - ml_fd).abs() <= 1e-5 * ml_hand.abs().max(1.0),
2410                "H_μls observed μ={mu} η={eta_ls} y={y}: hand={ml_hand} fd={ml_fd}"
2411            );
2412            assert!(
2413                (ll_hand - ll_fd).abs() <= 1e-5 * ll_hand.abs().max(1.0),
2414                "H_lsls observed μ={mu} η={eta_ls} y={y}: hand={ll_hand} fd={ll_fd}"
2415            );
2416        }
2417    }
2418
2419    #[test]
2420    fn first_directional_weights_match_observed_finite_difference() {
2421        // Pin (w_u, c_u, d_u) to a central FD of the observed coefficients
2422        // along the β-direction (μ += t·ξμ, η_ls += t·ξls).
2423        let cases = [
2424            (0.3_f64, -0.4_f64, 1.0_f64, 0.5_f64, -0.7_f64, 0.55_f64),
2425            (-1.2, 0.7, 2.5, 1.1, 0.3, -0.9),
2426            (0.0, 1.5, 0.4, -0.2, 0.9, 0.35),
2427            (2.4, -1.1, 0.8, 0.6, -0.4, 1.7),
2428        ];
2429        let t = 1e-6;
2430        for &(mu, eta_ls, a, xi_mu, xi_ls, y) in &cases {
2431            let rows =
2432                gaussian_jointrow_scalars(&array![y], &array![mu], &array![eta_ls], &array![a])
2433                    .expect("row scalars");
2434            let (w_u, c_u, d_u) =
2435                gaussian_joint_first_directionalweights(&rows, &array![xi_mu], &array![xi_ls]);
2436            let coeffs_at =
2437                |m: f64, e: f64| -> (f64, f64, f64) { production_observed_row(y, m, e, a) };
2438            let (mmp, mlp, llp) = coeffs_at(mu + t * xi_mu, eta_ls + t * xi_ls);
2439            let (mmm, mlm, llm) = coeffs_at(mu - t * xi_mu, eta_ls - t * xi_ls);
2440            let fd_w = (mmp - mmm) / (2.0 * t);
2441            let fd_c = (mlp - mlm) / (2.0 * t);
2442            let fd_d = (llp - llm) / (2.0 * t);
2443            assert!(
2444                (w_u[0] - fd_w).abs() <= 1e-5 * fd_w.abs().max(1.0),
2445                "dH_μμ drift μ={mu} η={eta_ls}: hand={} fd={fd_w}",
2446                w_u[0]
2447            );
2448            assert!(
2449                (c_u[0] - fd_c).abs() <= 1e-5 * fd_c.abs().max(1.0),
2450                "dH_μls drift μ={mu} η={eta_ls}: hand={} fd={fd_c}",
2451                c_u[0]
2452            );
2453            assert!(
2454                (d_u[0] - fd_d).abs() <= 1e-5 * fd_d.abs().max(1.0),
2455                "dH_lsls drift μ={mu} η={eta_ls}: hand={} fd={fd_d}",
2456                d_u[0]
2457            );
2458        }
2459    }
2460
2461    #[test]
2462    fn second_directional_weights_match_first_directional_finite_difference() {
2463        // Pin (w_uv, c_uv, d_uv) to a central FD of the FIRST-directional
2464        // observed drifts along the v-direction.
2465        let cases = [
2466            (
2467                0.3_f64, -0.4_f64, 1.0_f64, 0.5_f64, -0.7_f64, 0.8_f64, 0.2_f64, 0.55_f64,
2468            ),
2469            (-1.2, 0.7, 2.5, 1.1, 0.3, -0.6, 0.9, -0.9),
2470            (0.0, 1.5, 0.4, -0.2, 0.9, 0.4, -0.5, 0.35),
2471        ];
2472        let t = 1e-5;
2473        for &(mu, eta_ls, a, xi_mu_u, xi_ls_u, xi_mu_v, xi_ls_v, y) in &cases {
2474            let rows =
2475                gaussian_jointrow_scalars(&array![y], &array![mu], &array![eta_ls], &array![a])
2476                    .expect("row scalars");
2477            let (w_uv, c_uv, d_uv) = gaussian_jointsecond_directionalweights(
2478                &rows,
2479                &array![xi_mu_u],
2480                &array![xi_ls_u],
2481                &array![xi_mu_v],
2482                &array![xi_ls_v],
2483            );
2484            let first_at = |m: f64, e: f64| -> (f64, f64, f64) {
2485                let r = gaussian_jointrow_scalars(&array![y], &array![m], &array![e], &array![a])
2486                    .expect("row scalars");
2487                let (w_u, c_u, d_u) =
2488                    gaussian_joint_first_directionalweights(&r, &array![xi_mu_u], &array![xi_ls_u]);
2489                (w_u[0], c_u[0], d_u[0])
2490            };
2491            let (wp, cp, dp) = first_at(mu + t * xi_mu_v, eta_ls + t * xi_ls_v);
2492            let (wm, cm, dm) = first_at(mu - t * xi_mu_v, eta_ls - t * xi_ls_v);
2493            let fd_w = (wp - wm) / (2.0 * t);
2494            let fd_c = (cp - cm) / (2.0 * t);
2495            let fd_d = (dp - dm) / (2.0 * t);
2496            assert!(
2497                (w_uv[0] - fd_w).abs() <= 1e-4 * fd_w.abs().max(1.0),
2498                "d²H_μμ drift μ={mu} η={eta_ls}: hand={} fd={fd_w}",
2499                w_uv[0]
2500            );
2501            assert!(
2502                (c_uv[0] - fd_c).abs() <= 1e-4 * fd_c.abs().max(1.0),
2503                "d²H_μls drift μ={mu} η={eta_ls}: hand={} fd={fd_c}",
2504                c_uv[0]
2505            );
2506            assert!(
2507                (d_uv[0] - fd_d).abs() <= 1e-4 * fd_d.abs().max(1.0),
2508                "d²H_lsls drift μ={mu} η={eta_ls}: hand={} fd={fd_d}",
2509                d_uv[0]
2510            );
2511        }
2512    }
2513
2514    /// #932 jet oracle for the LIVE gaulss third/fourth directional Hessian
2515    /// drifts. The FD tests above are the independent numerical witness; these
2516    /// two pin the SAME live hand closed forms
2517    /// (`gaussian_joint_first_directionalweights` /
2518    /// `gaussian_jointsecond_directionalweights`) against the universal gam-math
2519    /// Taylor jet — the mechanical single source — at ≤1e-9, closing the audit
2520    /// gap that the gam-math gaulss oracle covered only value/∇/observed-H.
2521    mod jet_third_fourth_oracle {
2522        use super::*;
2523        use gam_math::jet_tower::{program_fourth_contracted, program_third_contracted};
2524
2525        fn close(hand: f64, jet: f64, label: &str) {
2526            let band = 1e-9 + 1e-9 * hand.abs().max(jet.abs());
2527            assert!(
2528                (hand - jet).abs() <= band,
2529                "{label}: hand {hand:+.15e} vs jet {jet:+.15e} (band {band:.3e})"
2530            );
2531        }
2532
2533        /// `gaussian_joint_first_directionalweights` (the LIVE third-order ∂_dir
2534        /// of the observed Hessian) equals the jet's contracted third at ≤1e-9.
2535        #[test]
2536        fn first_directional_weights_match_jet_third() {
2537            let cases = [
2538                (0.3_f64, -0.4_f64, 1.0_f64, 0.5_f64, -0.7_f64, 0.55_f64),
2539                (-1.2, 0.7, 2.5, 1.1, 0.3, -0.9),
2540                (0.0, 1.5, 0.4, -0.2, 0.9, 0.35),
2541                (2.4, -1.1, 0.8, 0.6, -0.4, 1.7),
2542                (-0.6, 0.2, 3.3, 0.8, -1.0, -1.1),
2543            ];
2544            for &(mu, eta_ls, a, xi_mu, xi_ls, y) in &cases {
2545                let rows =
2546                    gaussian_jointrow_scalars(&array![y], &array![mu], &array![eta_ls], &array![a])
2547                        .expect("row scalars");
2548                let (w_u, c_u, d_u) =
2549                    gaussian_joint_first_directionalweights(&rows, &array![xi_mu], &array![xi_ls]);
2550                let prog = crate::gamlss::GaussianJointRowProgram::new(&rows);
2551                let jt = program_third_contracted(&prog, 0, &[xi_mu, xi_ls]).expect("jet third");
2552                close(w_u[0], jt[0][0], &format!("dH_μμ μ={mu} η={eta_ls}"));
2553                close(c_u[0], jt[0][1], &format!("dH_μls μ={mu} η={eta_ls}"));
2554                close(c_u[0], jt[1][0], &format!("dH_lsμ μ={mu} η={eta_ls}"));
2555                close(d_u[0], jt[1][1], &format!("dH_lsls μ={mu} η={eta_ls}"));
2556            }
2557        }
2558
2559        /// `gaussian_jointsecond_directionalweights` (the LIVE fourth-order
2560        /// ∂_u∂_v of the observed Hessian) equals the jet's contracted fourth at
2561        /// ≤1e-9.
2562        #[test]
2563        fn second_directional_weights_match_jet_fourth() {
2564            let cases = [
2565                (
2566                    0.3_f64, -0.4_f64, 1.0_f64, 0.5_f64, -0.7_f64, 0.8_f64, 0.2_f64, 0.55_f64,
2567                ),
2568                (-1.2, 0.7, 2.5, 1.1, 0.3, -0.6, 0.9, -0.9),
2569                (0.0, 1.5, 0.4, -0.2, 0.9, 0.4, -0.5, 0.35),
2570                (2.4, -1.1, 0.8, 0.6, -0.4, -0.3, 0.7, 1.7),
2571            ];
2572            for &(mu, eta_ls, a, xi_mu_u, xi_ls_u, xi_mu_v, xi_ls_v, y) in &cases {
2573                let rows =
2574                    gaussian_jointrow_scalars(&array![y], &array![mu], &array![eta_ls], &array![a])
2575                        .expect("row scalars");
2576                let (w_uv, c_uv, d_uv) = gaussian_jointsecond_directionalweights(
2577                    &rows,
2578                    &array![xi_mu_u],
2579                    &array![xi_ls_u],
2580                    &array![xi_mu_v],
2581                    &array![xi_ls_v],
2582                );
2583                let prog = crate::gamlss::GaussianJointRowProgram::new(&rows);
2584                let jt =
2585                    program_fourth_contracted(&prog, 0, &[xi_mu_u, xi_ls_u], &[xi_mu_v, xi_ls_v])
2586                        .expect("jet fourth");
2587                close(w_uv[0], jt[0][0], &format!("d²H_μμ μ={mu} η={eta_ls}"));
2588                close(c_uv[0], jt[0][1], &format!("d²H_μls μ={mu} η={eta_ls}"));
2589                close(c_uv[0], jt[1][0], &format!("d²H_lsμ μ={mu} η={eta_ls}"));
2590                close(d_uv[0], jt[1][1], &format!("d²H_lsls μ={mu} η={eta_ls}"));
2591            }
2592        }
2593
2594        /// #932 release speed gate for the Gaussian location-scale joint row.
2595        /// The production structure-compiled order-2/third/fourth lowerings
2596        /// ([`GaussianJointRowProgram::row_order2`] /
2597        /// `row_third_contracted` / `row_fourth_contracted`, all emitted from
2598        /// the one [`gaussian_normalized_row`] declaration) are timed against
2599        /// the generic gam-math forward-mode jet tower ([`program_row_kernel`]
2600        /// / [`program_third_contracted`] / [`program_fourth_contracted`]) —
2601        /// the naive automatic-differentiation baseline the retained
2602        /// specialization must beat; #932 keeps no separate `cfg(test)` hand
2603        /// restatement for this family. Emits one harness-parsed
2604        /// `generic_tower_over_production` diagnostic (generic-tower time over production
2605        /// time) per derivative channel; the MSI release harness fails closed
2606        /// whenever any measured cell is `<= 1`.
2607        ///
2608        /// The batch carries distinct certified per-row scalars and distinct
2609        /// per-row directions, so the optimizer cannot hoist the pure row
2610        /// calls out of the sweeps, and the finite checksum over every
2611        /// returned channel keeps each sweep live without
2612        /// `std::hint::black_box`.
2613        #[test]
2614        fn release_measure_gaussian_joint_specialized_vs_generic_tower_932() {
2615            use gam_math::jet_tower::program_row_kernel;
2616            use std::time::Instant;
2617
2618            const ROWS: usize = 512;
2619            let y = Array1::from_shape_fn(ROWS, |i| {
2620                let f = i as f64;
2621                1.4 * (f * 0.17 + 0.3).sin() - 0.5 * (f * 0.09).cos()
2622            });
2623            let mu = Array1::from_shape_fn(ROWS, |i| {
2624                let f = i as f64;
2625                0.9 * (f * 0.13 + 0.7).cos() + 0.3 * (f * 0.05).sin()
2626            });
2627            let eta_ls = Array1::from_shape_fn(ROWS, |i| {
2628                let f = i as f64;
2629                0.8 * (f * 0.11 + 0.2).sin() - 0.25 * (f * 0.07).cos()
2630            });
2631            let weight = Array1::from_shape_fn(ROWS, |i| {
2632                let f = i as f64;
2633                0.6 + 0.4 * (f * 0.19 + 1.0).sin().abs()
2634            });
2635            let rows = gaussian_jointrow_scalars(&y, &mu, &eta_ls, &weight)
2636                .expect("certified release-measure Gaussian joint rows");
2637            let program = crate::gamlss::GaussianJointRowProgram::new(&rows);
2638            let dir_u: Vec<[f64; 2]> = (0..ROWS)
2639                .map(|i| {
2640                    let f = i as f64;
2641                    [
2642                        0.7 * (f * 0.23 + 0.4).cos() - 0.2 * (f * 0.03).sin(),
2643                        -0.6 * (f * 0.29 + 0.1).sin() + 0.25 * (f * 0.15).cos(),
2644                    ]
2645                })
2646                .collect();
2647            let dir_v: Vec<[f64; 2]> = (0..ROWS)
2648                .map(|i| {
2649                    let f = i as f64;
2650                    [
2651                        -0.5 * (f * 0.21 + 0.9).sin() + 0.3 * (f * 0.06).cos(),
2652                        0.8 * (f * 0.27 + 0.5).cos() - 0.15 * (f * 0.04).sin(),
2653                    ]
2654                })
2655                .collect();
2656
2657            // Warm both paths and pin equal V/G/H plus equal contracted
2658            // third/fourth, so each timed pair measures equal work.
2659            for row in 0..ROWS {
2660                let atom = program.row_order2(row);
2661                let (tower_value, tower_gradient, tower_hessian) =
2662                    program_row_kernel(&program, row).expect("tower warm kernel");
2663                close(atom.value(), tower_value, "release-measure value parity");
2664                let production_gradient = atom.gradient();
2665                for a in 0..2 {
2666                    close(
2667                        production_gradient[a],
2668                        tower_gradient[a],
2669                        "release-measure gradient parity",
2670                    );
2671                    for b in 0..2 {
2672                        close(
2673                            atom.hessian_at(a, b),
2674                            tower_hessian[a][b],
2675                            "release-measure hessian parity",
2676                        );
2677                    }
2678                }
2679                let production_third = program.row_third_contracted(row, &dir_u[row]);
2680                let tower_third = program_third_contracted(&program, row, &dir_u[row])
2681                    .expect("tower warm third");
2682                let production_fourth =
2683                    program.row_fourth_contracted(row, &dir_u[row], &dir_v[row]);
2684                let tower_fourth =
2685                    program_fourth_contracted(&program, row, &dir_u[row], &dir_v[row])
2686                        .expect("tower warm fourth");
2687                for a in 0..2 {
2688                    for b in 0..2 {
2689                        close(
2690                            production_third[a][b],
2691                            tower_third[a][b],
2692                            "release-measure third parity",
2693                        );
2694                        close(
2695                            production_fourth[a][b],
2696                            tower_fourth[a][b],
2697                            "release-measure fourth parity",
2698                        );
2699                    }
2700                }
2701            }
2702
2703            let best_secs = |sweep: &mut dyn FnMut() -> f64| -> f64 {
2704                let mut best = f64::INFINITY;
2705                for _ in 0..5 {
2706                    let started = Instant::now();
2707                    let checksum = sweep();
2708                    assert!(
2709                        checksum.is_finite(),
2710                        "Gaussian joint release-measure checksum must stay finite"
2711                    );
2712                    best = best.min(started.elapsed().as_secs_f64());
2713                }
2714                best
2715            };
2716
2717            let mut production_order2_sweep = || {
2718                let mut checksum = 0.0_f64;
2719                for row in 0..ROWS {
2720                    let atom = program.row_order2(row);
2721                    checksum += atom.value() + atom.gradient()[0] + atom.hessian_at(0, 0);
2722                }
2723                checksum
2724            };
2725            let production_order2_secs = best_secs(&mut production_order2_sweep);
2726            let mut tower_order2_sweep = || {
2727                let mut checksum = 0.0_f64;
2728                for row in 0..ROWS {
2729                    let (value, gradient, hessian) =
2730                        program_row_kernel(&program, row).expect("tower kernel");
2731                    checksum += value + gradient[0] + hessian[0][0];
2732                }
2733                checksum
2734            };
2735            let tower_order2_secs = best_secs(&mut tower_order2_sweep);
2736
2737            let mut production_third_sweep = || {
2738                let mut checksum = 0.0_f64;
2739                for row in 0..ROWS {
2740                    let third = program.row_third_contracted(row, &dir_u[row]);
2741                    checksum += third[0][0] + third[0][1] + third[1][1];
2742                }
2743                checksum
2744            };
2745            let production_third_secs = best_secs(&mut production_third_sweep);
2746            let mut tower_third_sweep = || {
2747                let mut checksum = 0.0_f64;
2748                for row in 0..ROWS {
2749                    let third = program_third_contracted(&program, row, &dir_u[row])
2750                        .expect("tower third");
2751                    checksum += third[0][0] + third[0][1] + third[1][1];
2752                }
2753                checksum
2754            };
2755            let tower_third_secs = best_secs(&mut tower_third_sweep);
2756
2757            let mut production_fourth_sweep = || {
2758                let mut checksum = 0.0_f64;
2759                for row in 0..ROWS {
2760                    let fourth = program.row_fourth_contracted(row, &dir_u[row], &dir_v[row]);
2761                    checksum += fourth[0][0] + fourth[0][1] + fourth[1][1];
2762                }
2763                checksum
2764            };
2765            let production_fourth_secs = best_secs(&mut production_fourth_sweep);
2766            let mut tower_fourth_sweep = || {
2767                let mut checksum = 0.0_f64;
2768                for row in 0..ROWS {
2769                    let fourth =
2770                        program_fourth_contracted(&program, row, &dir_u[row], &dir_v[row])
2771                            .expect("tower fourth");
2772                    checksum += fourth[0][0] + fourth[0][1] + fourth[1][1];
2773                }
2774                checksum
2775            };
2776            let tower_fourth_secs = best_secs(&mut tower_fourth_sweep);
2777
2778            for (channel, production_secs, tower_secs) in [
2779                ("order2", production_order2_secs, tower_order2_secs),
2780                ("third", production_third_secs, tower_third_secs),
2781                ("fourth", production_fourth_secs, tower_fourth_secs),
2782            ] {
2783                let production_ns = production_secs * 1e9 / ROWS as f64;
2784                let tower_ns = tower_secs * 1e9 / ROWS as f64;
2785                eprintln!(
2786                    "GAUSSIAN-JOINT-RELEASE-932 channel={channel} rows={ROWS} \
2787                     production_ns={production_ns:.3} generic_tower_ns={tower_ns:.3} \
2788                     generic_tower_over_production={:.6}",
2789                    tower_ns / production_ns,
2790                );
2791            }
2792        }
2793    }
2794}
2795
2796/// The three `CustomFamily` joint-ψ hooks, generated once for every
2797/// location-scale family already on the normalised `_for_specs` signature.
2798///
2799/// These three hooks are a family's exact likelihood-side contribution to the
2800/// unified full `[rho, psi]` outer Hessian:
2801///
2802///   `exact_newton_joint_psi_terms(..)`                          -> `D_a`, `D_{beta a}`, `D_{beta beta a}`
2803///   `exact_newton_joint_psisecond_order_terms(..)`              -> `D_ab`, `D_{beta ab}`, `D_{beta beta ab}`
2804///   `exact_newton_joint_psihessian_directional_derivative(..)`  -> `T_a[u]`
2805///
2806/// Generic exact-joint code in `custom_family.rs` adds all realized penalty
2807/// motion `S_a` / `S_ab` and combines these likelihood-only objects with the
2808/// joint mode solves `beta_i`, `beta_ij` and the total Hessian drifts
2809/// `dot H_i`, `ddot H_ij`. Keeping this contract explicit is what makes a
2810/// wiggle family's full `[rho, psi]` Hessian real rather than a gradient-only
2811/// or block-local surrogate.
2812///
2813/// (That contract was written out on exactly one of the four copies, in
2814/// `binomial/wiggle_custom_family.rs`. Collapsing the copies would have
2815/// deleted the only statement of it in the tree, so it moved here — #2470.)
2816///
2817/// Each body is a family-axis guard plus a forward into the family's
2818/// `_for_specs` entry. The guard is the one that survives: the `_for_specs`
2819/// methods themselves no longer re-check `family_axis_count()`, because these
2820/// hooks are their only callers.
2821macro_rules! impl_location_scale_joint_psi_custom_family_hooks {
2822    ($label:literal) => {
2823        fn exact_newton_joint_psi_terms(
2824            &self,
2825            block_states: &[ParameterBlockState],
2826            specs: &[ParameterBlockSpec],
2827            hyper_layout: &crate::custom_family::CustomFamilyHyperLayout,
2828            psi_index: usize,
2829        ) -> Result<Option<gam_problem::ExactNewtonJointPsiTerms>, String> {
2830            if hyper_layout.family_axis_count() != 0 {
2831                return Err(concat!($label, " does not declare family-owned hyper axes").to_string());
2832            }
2833            self.exact_newton_joint_psi_terms_for_specs(
2834                block_states,
2835                specs,
2836                hyper_layout.design_derivative_blocks(),
2837                psi_index,
2838            )
2839        }
2840
2841        fn exact_newton_joint_psisecond_order_terms(
2842            &self,
2843            block_states: &[ParameterBlockState],
2844            specs: &[ParameterBlockSpec],
2845            hyper_layout: &crate::custom_family::CustomFamilyHyperLayout,
2846            psi_i: usize,
2847            psi_j: usize,
2848        ) -> Result<Option<gam_problem::ExactNewtonJointPsiSecondOrderTerms>, String> {
2849            if hyper_layout.family_axis_count() != 0 {
2850                return Err(concat!($label, " does not declare family-owned hyper axes").to_string());
2851            }
2852            self.exact_newton_joint_psisecond_order_terms_for_specs(
2853                block_states,
2854                specs,
2855                hyper_layout.design_derivative_blocks(),
2856                psi_i,
2857                psi_j,
2858            )
2859        }
2860
2861        fn exact_newton_joint_psihessian_directional_derivative(
2862            &self,
2863            block_states: &[ParameterBlockState],
2864            specs: &[ParameterBlockSpec],
2865            hyper_layout: &crate::custom_family::CustomFamilyHyperLayout,
2866            psi_index: usize,
2867            d_beta_flat: &Array1<f64>,
2868        ) -> Result<Option<Array2<f64>>, String> {
2869            if hyper_layout.family_axis_count() != 0 {
2870                return Err(concat!($label, " does not declare family-owned hyper axes").to_string());
2871            }
2872            self.exact_newton_joint_psihessian_directional_derivative_for_specs(
2873                block_states,
2874                specs,
2875                hyper_layout.design_derivative_blocks(),
2876                psi_index,
2877                d_beta_flat,
2878            )
2879        }
2880    };
2881}
2882
2883pub(crate) use impl_location_scale_joint_psi_custom_family_hooks;