1use super::*;
6
7pub(crate) struct GaussianLocationScaleWiggleGeometry {
8 pub(crate) basis: Array2<f64>,
9 pub(crate) basis_d1: Array2<f64>,
10 pub(crate) basis_d2: Array2<f64>,
11 pub(crate) basis_d3: Array2<f64>,
12 pub(crate) dq_dq0: Array1<f64>,
13 pub(crate) d2q_dq02: Array1<f64>,
14 pub(crate) d3q_dq03: Array1<f64>,
15 pub(crate) d4q_dq04: Array1<f64>,
16}
17
18pub(crate) struct GaussianLocationScaleWiggleHessianRowPieces {
22 pub(crate) coeff_mm: Array1<f64>,
23 pub(crate) coeff_ml: Array1<f64>,
24 pub(crate) coeff_ll: Array1<f64>,
25 pub(crate) coeff_mw_b: Array1<f64>,
26 pub(crate) coeff_mw_d: Array1<f64>,
27 pub(crate) coeff_lw_b: Array1<f64>,
28 pub(crate) coeff_ww: Array1<f64>,
29 pub(crate) basis: Array2<f64>,
30 pub(crate) basis_d1: Array2<f64>,
31}
32
33impl GaussianLocationScaleWiggleHessianRowPieces {
34 pub(crate) fn assemble_dense(
35 &self,
36 xmu: &Array2<f64>,
37 x_ls: &Array2<f64>,
38 ) -> Result<Array2<f64>, String> {
39 let h_mm = xt_diag_x_dense(xmu, &self.coeff_mm)?;
40 let h_ml = xt_diag_y_dense(xmu, &self.coeff_ml, x_ls)?;
41 let h_ll = xt_diag_x_dense(x_ls, &self.coeff_ll)?;
42 let h_mw = xt_diag_y_dense(xmu, &self.coeff_mw_b, &self.basis)?
43 + &xt_diag_y_dense(xmu, &self.coeff_mw_d, &self.basis_d1)?;
44 let h_lw = xt_diag_y_dense(x_ls, &self.coeff_lw_b, &self.basis)?;
45 let h_ww = xt_diag_x_dense(&self.basis, &self.coeff_ww)?;
46 Ok(gaussian_pack_wiggle_joint_symmetrichessian(
47 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
48 ))
49 }
50}
51
52pub struct GaussianLocationScaleWiggleFamily {
53 pub y: Array1<f64>,
54 pub weights: Array1<f64>,
55 pub mu_design: Option<DesignMatrix>,
56 pub log_sigma_design: Option<DesignMatrix>,
57 pub wiggle_knots: Array1<f64>,
58 pub wiggle_degree: usize,
59 pub policy: gam_runtime::resource::ResourcePolicy,
64 pub(crate) cached_row_scalars:
65 std::sync::RwLock<Option<(f64, f64, f64, f64, f64, f64, Arc<GaussianJointRowScalars>)>>,
66}
67
68impl Clone for GaussianLocationScaleWiggleFamily {
69 fn clone(&self) -> Self {
70 Self {
71 y: self.y.clone(),
72 weights: self.weights.clone(),
73 mu_design: self.mu_design.clone(),
74 log_sigma_design: self.log_sigma_design.clone(),
75 wiggle_knots: self.wiggle_knots.clone(),
76 wiggle_degree: self.wiggle_degree,
77 policy: self.policy.clone(),
78 cached_row_scalars: std::sync::RwLock::new(
79 self.cached_row_scalars
80 .read()
81 .expect("lock poisoned")
82 .clone(),
83 ),
84 }
85 }
86}
87
88impl GaussianLocationScaleWiggleFamily {
89 pub const BLOCK_MU: usize = 0;
90 pub const BLOCK_LOG_SIGMA: usize = 1;
91 pub const BLOCK_WIGGLE: usize = 2;
92
93 pub fn parameternames() -> &'static [&'static str] {
94 &["mu", "log_sigma", "wiggle"]
95 }
96
97 pub fn parameter_links() -> &'static [ParameterLink] {
98 &[
99 ParameterLink::Identity,
100 ParameterLink::Log,
101 ParameterLink::Wiggle,
102 ]
103 }
104
105 pub fn metadata() -> FamilyMetadata {
106 FamilyMetadata {
107 name: "gaussian_location_scalewiggle",
108 parameternames: Self::parameternames(),
109 parameter_links: Self::parameter_links(),
110 }
111 }
112
113 pub(crate) fn exact_joint_supported(&self) -> bool {
114 self.mu_design.is_some() && self.log_sigma_design.is_some()
115 }
116
117 pub(crate) fn wiggle_basiswith_options(
118 &self,
119 q0: ArrayView1<'_, f64>,
120 options: BasisOptions,
121 ) -> Result<Array2<f64>, String> {
122 monotone_wiggle_basis_with_derivative_order(
123 q0,
124 &self.wiggle_knots,
125 self.wiggle_degree,
126 options.derivative_order,
127 )
128 }
129
130 pub(crate) fn wiggle_design(&self, q0: ArrayView1<'_, f64>) -> Result<Array2<f64>, String> {
131 self.wiggle_basiswith_options(q0, BasisOptions::value())
132 }
133
134 pub(crate) fn wiggle_dq_dq0(
135 &self,
136 q0: ArrayView1<'_, f64>,
137 beta_link_wiggle: ArrayView1<'_, f64>,
138 ) -> Result<Array1<f64>, String> {
139 let d1 = self.wiggle_basiswith_options(q0, BasisOptions::first_derivative())?;
140 if d1.ncols() != beta_link_wiggle.len() {
141 return Err(GamlssError::DimensionMismatch { reason: format!(
142 "wiggle derivative/beta mismatch: basis has {} columns but beta_link_wiggle has {} coefficients",
143 d1.ncols(),
144 beta_link_wiggle.len()
145 ) }.into());
146 }
147 Ok(d1.dot(&beta_link_wiggle) + 1.0)
148 }
149
150 pub(crate) fn wiggle_d2q_dq02(
151 &self,
152 q0: ArrayView1<'_, f64>,
153 beta_link_wiggle: ArrayView1<'_, f64>,
154 ) -> Result<Array1<f64>, String> {
155 let d2 = self.wiggle_basiswith_options(q0, BasisOptions::second_derivative())?;
156 if d2.ncols() != beta_link_wiggle.len() {
157 return Err(GamlssError::DimensionMismatch { reason: format!(
158 "wiggle second-derivative/beta mismatch: basis has {} columns but beta_link_wiggle has {} coefficients",
159 d2.ncols(),
160 beta_link_wiggle.len()
161 ) }.into());
162 }
163 Ok(d2.dot(&beta_link_wiggle))
164 }
165
166 pub(crate) fn wiggle_d3basis_constrained(
167 &self,
168 q0: ArrayView1<'_, f64>,
169 ) -> Result<Array2<f64>, String> {
170 monotone_wiggle_basis_with_derivative_order(q0, &self.wiggle_knots, self.wiggle_degree, 3)
171 }
172
173 pub(crate) fn wiggle_d3q_dq03(
174 &self,
175 q0: ArrayView1<'_, f64>,
176 beta_link_wiggle: ArrayView1<'_, f64>,
177 ) -> Result<Array1<f64>, String> {
178 let d3 = self.wiggle_d3basis_constrained(q0)?;
179 if d3.ncols() != beta_link_wiggle.len() {
180 return Err(GamlssError::DimensionMismatch { reason: format!(
181 "wiggle third-derivative/beta mismatch: basis has {} columns but beta_link_wiggle has {} coefficients",
182 d3.ncols(),
183 beta_link_wiggle.len()
184 ) }.into());
185 }
186 Ok(d3.dot(&beta_link_wiggle))
187 }
188
189 pub(crate) fn wiggle_d4q_dq04(
190 &self,
191 q0: ArrayView1<'_, f64>,
192 beta_link_wiggle: ArrayView1<'_, f64>,
193 ) -> Result<Array1<f64>, String> {
194 let d4 = monotone_wiggle_basis_with_derivative_order(
195 q0,
196 &self.wiggle_knots,
197 self.wiggle_degree,
198 4,
199 )?;
200 if d4.ncols() != beta_link_wiggle.len() {
201 return Err(GamlssError::DimensionMismatch { reason: format!(
202 "wiggle fourth-derivative/beta mismatch: basis has {} columns but beta_link_wiggle has {} coefficients",
203 d4.ncols(),
204 beta_link_wiggle.len()
205 ) }.into());
206 }
207 Ok(d4.dot(&beta_link_wiggle))
208 }
209
210 pub(crate) fn wiggle_geometry(
211 &self,
212 q0: ArrayView1<'_, f64>,
213 beta_link_wiggle: ArrayView1<'_, f64>,
214 ) -> Result<GaussianLocationScaleWiggleGeometry, String> {
215 let basis = self.wiggle_design(q0)?;
216 let basis_d1 = self.wiggle_basiswith_options(q0, BasisOptions::first_derivative())?;
217 let basis_d2 = self.wiggle_basiswith_options(q0, BasisOptions::second_derivative())?;
218 let basis_d3 = self.wiggle_d3basis_constrained(q0)?;
219 let dq_dq0 = self.wiggle_dq_dq0(q0, beta_link_wiggle)?;
220 let d2q_dq02 = self.wiggle_d2q_dq02(q0, beta_link_wiggle)?;
221 let d3q_dq03 = self.wiggle_d3q_dq03(q0, beta_link_wiggle)?;
222 let d4q_dq04 = self.wiggle_d4q_dq04(q0, beta_link_wiggle)?;
223 Ok(GaussianLocationScaleWiggleGeometry {
224 basis,
225 basis_d1,
226 basis_d2,
227 basis_d3,
228 dq_dq0,
229 d2q_dq02,
230 d3q_dq03,
231 d4q_dq04,
232 })
233 }
234
235 pub(crate) fn get_or_compute_row_scalars(
236 &self,
237 q: &Array1<f64>,
238 eta_ls: &Array1<f64>,
239 ) -> Result<Arc<GaussianJointRowScalars>, String> {
240 Ok(Arc::new(gaussian_jointrow_scalars(
241 &self.y,
242 q,
243 eta_ls,
244 &self.weights,
245 )?))
246 }
247
248 pub(crate) fn dense_block_designs(
249 &self,
250 ) -> Result<(Cow<'_, Array2<f64>>, Cow<'_, Array2<f64>>), String> {
251 dense_locscale_block_designs_cached(
252 self.mu_design.as_ref(),
253 self.log_sigma_design.as_ref(),
254 "GaussianLocationScaleWiggleFamily",
255 "GaussianLocationScaleWiggle",
256 "mu",
257 &self.policy.material_policy(),
258 )
259 }
260 pub(crate) fn dense_block_designs_fromspecs<'a>(
261 &self,
262 specs: &'a [ParameterBlockSpec],
263 ) -> Result<(Cow<'a, Array2<f64>>, Cow<'a, Array2<f64>>), String> {
264 dense_locscale_block_designs_fromspecs(
265 specs,
266 3,
267 "GaussianLocationScaleWiggleFamily",
268 "GaussianLocationScaleWiggle",
269 Self::BLOCK_MU,
270 Self::BLOCK_LOG_SIGMA,
271 "mu",
272 &self.policy.material_policy(),
273 )
274 }
275
276 pub(crate) fn exact_joint_dense_block_designs<'a>(
277 &'a self,
278 specs: Option<&'a [ParameterBlockSpec]>,
279 ) -> Result<Option<(Cow<'a, Array2<f64>>, Cow<'a, Array2<f64>>)>, String> {
280 if self.exact_joint_supported() {
281 return self.dense_block_designs().map(Some);
282 }
283 if let Some(specs) = specs {
284 return self.dense_block_designs_fromspecs(specs).map(Some);
285 }
286 Ok(None)
287 }
288
289 pub fn block_effective_jacobian(
299 specs: &[ParameterBlockSpec],
300 block_idx: usize,
301 ) -> Result<Box<dyn BlockEffectiveJacobian>, String> {
302 crate::block_layout::block_jacobian::AdditiveWiggleBlockLayout {
303 family: "GaussianLocationScaleWiggleFamily",
304 n_outputs: 2,
305 additive_blocks: &[Self::BLOCK_MU, Self::BLOCK_LOG_SIGMA],
306 wiggle_block: Some(Self::BLOCK_WIGGLE),
307 }
308 .block_effective_jacobian(specs, block_idx)
309 }
310}
311
312pub(crate) struct GlsWiggleSecondDirCoeffs {
317 pub(crate) objective_uv: Array1<f64>,
318 pub(crate) coeff_mm_base: Array1<f64>,
319 pub(crate) coeff_mm_u: Array1<f64>,
320 pub(crate) coeff_mm_v: Array1<f64>,
321 pub(crate) coeff_mm_uv: Array1<f64>,
322 pub(crate) coeff_ml_base: Array1<f64>,
323 pub(crate) coeff_ml_u: Array1<f64>,
324 pub(crate) coeff_ml_v: Array1<f64>,
325 pub(crate) coeff_ml_uv: Array1<f64>,
326 pub(crate) coeff_ll_base: Array1<f64>,
327 pub(crate) coeff_ll_u: Array1<f64>,
328 pub(crate) coeff_ll_v: Array1<f64>,
329 pub(crate) coeff_ll_uv: Array1<f64>,
330 pub(crate) mean_wiggle_base: Array1<f64>,
331 pub(crate) a_u: Array1<f64>,
332 pub(crate) a_v: Array1<f64>,
333 pub(crate) a_uv: Array1<f64>,
334 pub(crate) c_u: Array1<f64>,
335 pub(crate) c_v: Array1<f64>,
336 pub(crate) c_uv: Array1<f64>,
337 pub(crate) gradient_ls_base: Array1<f64>,
338 pub(crate) gradient_ls_u: Array1<f64>,
339 pub(crate) gradient_ls_v: Array1<f64>,
340 pub(crate) gradient_ls_uv: Array1<f64>,
341 pub(crate) l_u: Array1<f64>,
342 pub(crate) l_v: Array1<f64>,
343 pub(crate) l_uv: Array1<f64>,
344 pub(crate) hessian_mm_base: Array1<f64>,
345 pub(crate) gradient_mu_base: Array1<f64>,
346 pub(crate) hessian_ml_base: Array1<f64>,
347 pub(crate) hessian_mm_u: Array1<f64>,
348 pub(crate) hessian_mm_v: Array1<f64>,
349 pub(crate) hessian_mm_uv: Array1<f64>,
350}
351
352pub(crate) struct GlsWiggleFirstDirCoeffs {
353 pub(crate) coeff_mm_base: Array1<f64>,
354 pub(crate) coeff_ml_base: Array1<f64>,
355 pub(crate) coeff_ll_base: Array1<f64>,
356 pub(crate) coeff_mm_u: Array1<f64>,
357 pub(crate) coeff_ml_u: Array1<f64>,
358 pub(crate) coeff_ll_u: Array1<f64>,
359 pub(crate) mean_wiggle_u: Array1<f64>,
360 pub(crate) gradient_mu_u: Array1<f64>,
361 pub(crate) scale_wiggle_u: Array1<f64>,
362 pub(crate) mean_wiggle_base: Array1<f64>,
363 pub(crate) gradient_mu_base: Array1<f64>,
364 pub(crate) gradient_ls_base: Array1<f64>,
365 pub(crate) gradient_ls_u: Array1<f64>,
366 pub(crate) scale_wiggle_base: Array1<f64>,
367 pub(crate) hessian_mm_base: Array1<f64>,
368 pub(crate) hessian_mm_u: Array1<f64>,
369}
370
371pub(crate) fn gls_wiggle_first_directional_coeffs(
372 rows: &GaussianJointRowScalars,
373 geom: &GaussianLocationScaleWiggleGeometry,
374 q_u: &Array1<f64>,
375 zeta_u: &Array1<f64>,
376 s1_u: &Array1<f64>,
377 g2_u: &Array1<f64>,
378) -> GlsWiggleFirstDirCoeffs {
379 let tower = gaussian_row_first_tower(rows, q_u, zeta_u);
380 let base = &tower.base;
381 let first = &tower.first;
382 let d = &geom.dq_dq0;
383 let coeff_mm_base =
384 &base.hessian_mm * &d.mapv(|value| value * value) + &base.gradient_mu * &geom.d2q_dq02;
385 let coeff_ml_base = &base.hessian_ml * d;
386 let coeff_ll_base = base.hessian_ll.clone();
387 let coeff_mm_u = &first.hessian_mm * &d.mapv(|value| value * value)
388 + &(2.0 * &base.hessian_mm * d * s1_u)
389 + &(&first.gradient_mu * &geom.d2q_dq02)
390 + &(&base.gradient_mu * g2_u);
391 let coeff_ml_u = &first.hessian_ml * d + &base.hessian_ml * s1_u;
392 let coeff_ll_u = first.hessian_ll.clone();
393 let mean_wiggle_u = &first.hessian_mm * d + &base.hessian_mm * s1_u;
394 let gradient_mu_u = first.gradient_mu.clone();
395 let scale_wiggle_u = first.hessian_ml.clone();
396 let mean_wiggle_base = &base.hessian_mm * d;
397 GlsWiggleFirstDirCoeffs {
398 coeff_mm_base,
399 coeff_ml_base,
400 coeff_ll_base,
401 coeff_mm_u,
402 coeff_ml_u,
403 coeff_ll_u,
404 mean_wiggle_u,
405 gradient_mu_u,
406 scale_wiggle_u,
407 mean_wiggle_base,
408 gradient_mu_base: base.gradient_mu.clone(),
409 gradient_ls_base: base.gradient_ls.clone(),
410 gradient_ls_u: first.gradient_ls.clone(),
411 scale_wiggle_base: base.hessian_ml.clone(),
412 hessian_mm_base: base.hessian_mm.clone(),
413 hessian_mm_u: first.hessian_mm.clone(),
414 }
415}
416
417pub(crate) struct GlsWiggleDirPieces<'a> {
421 pub(crate) zeta_u: &'a Array1<f64>,
422 pub(crate) zeta_v: &'a Array1<f64>,
423 pub(crate) zeta_uv: &'a Array1<f64>,
424 pub(crate) q_u: &'a Array1<f64>,
425 pub(crate) q_v: &'a Array1<f64>,
426 pub(crate) q_uv: &'a Array1<f64>,
427 pub(crate) s1_u: &'a Array1<f64>,
428 pub(crate) s1_v: &'a Array1<f64>,
429 pub(crate) s1_uv: &'a Array1<f64>,
430 pub(crate) g2_u: &'a Array1<f64>,
431 pub(crate) g2_v: &'a Array1<f64>,
432 pub(crate) g2_uv: &'a Array1<f64>,
433}
434
435pub(crate) fn gls_wiggle_second_directional_coeffs(
438 rows: &GaussianJointRowScalars,
439 geom: &GaussianLocationScaleWiggleGeometry,
440 dir: &GlsWiggleDirPieces<'_>,
441) -> GlsWiggleSecondDirCoeffs {
442 let GlsWiggleDirPieces {
443 zeta_u,
444 zeta_v,
445 zeta_uv,
446 q_u,
447 q_v,
448 q_uv,
449 s1_u,
450 s1_v,
451 s1_uv,
452 g2_u,
453 g2_v,
454 g2_uv,
455 } = *dir;
456 let tower = gaussian_row_second_tower(rows, q_u, zeta_u, q_v, zeta_v, q_uv, zeta_uv);
457 let base = &tower.base;
458 let first_u = &tower.first_a;
459 let first_v = &tower.first_b;
460 let second_uv = &tower.second;
461 let d = &geom.dq_dq0;
462 let d2 = &geom.d2q_dq02;
463 let d_squared = d.mapv(|value| value * value);
464 let objective_uv = &base.hessian_mm * &(q_u * q_v)
465 + &base.hessian_ml * &(q_u * zeta_v + q_v * zeta_u)
466 + &base.hessian_ll * &(zeta_u * zeta_v)
467 + &base.gradient_mu * q_uv
468 + &base.gradient_ls * zeta_uv;
469 let coeff_mm_base = &base.hessian_mm * &d_squared + &base.gradient_mu * d2;
470 let coeff_mm_u = &first_u.hessian_mm * &d_squared
471 + &(2.0 * &base.hessian_mm * d * s1_u)
472 + &(&first_u.gradient_mu * d2)
473 + &(&base.gradient_mu * g2_u);
474 let coeff_mm_v = &first_v.hessian_mm * &d_squared
475 + &(2.0 * &base.hessian_mm * d * s1_v)
476 + &(&first_v.gradient_mu * d2)
477 + &(&base.gradient_mu * g2_v);
478 let coeff_mm_uv = &(&second_uv.hessian_mm * &d_squared)
479 + &(2.0 * &first_u.hessian_mm * d * s1_v)
480 + &(2.0 * &first_v.hessian_mm * d * s1_u)
481 + &(2.0 * &base.hessian_mm * s1_u * s1_v)
482 + &(2.0 * &base.hessian_mm * d * s1_uv)
483 + &(&second_uv.gradient_mu * d2)
484 + &(&first_u.gradient_mu * g2_v)
485 + &(&first_v.gradient_mu * g2_u)
486 + &(&base.gradient_mu * g2_uv);
487 let coeff_ml_base = &base.hessian_ml * d;
488 let coeff_ml_u = &first_u.hessian_ml * d + &base.hessian_ml * s1_u;
489 let coeff_ml_v = &first_v.hessian_ml * d + &base.hessian_ml * s1_v;
490 let coeff_ml_uv = &(&second_uv.hessian_ml * d)
491 + &(&first_u.hessian_ml * s1_v)
492 + &(&first_v.hessian_ml * s1_u)
493 + &(&base.hessian_ml * s1_uv);
494 let coeff_ll_base = base.hessian_ll.clone();
495 let coeff_ll_u = first_u.hessian_ll.clone();
496 let coeff_ll_v = first_v.hessian_ll.clone();
497 let coeff_ll_uv = second_uv.hessian_ll.clone();
498 let mean_wiggle_base = &base.hessian_mm * d;
499 let a_u = &first_u.hessian_mm * d + &base.hessian_mm * s1_u;
500 let a_v = &first_v.hessian_mm * d + &base.hessian_mm * s1_v;
501 let a_uv = &second_uv.hessian_mm * d
502 + &first_u.hessian_mm * s1_v
503 + &first_v.hessian_mm * s1_u
504 + &base.hessian_mm * s1_uv;
505 let c_u = first_u.gradient_mu.clone();
506 let c_v = first_v.gradient_mu.clone();
507 let c_uv = second_uv.gradient_mu.clone();
508 let l_u = first_u.hessian_ml.clone();
509 let l_v = first_v.hessian_ml.clone();
510 let l_uv = second_uv.hessian_ml.clone();
511
512 GlsWiggleSecondDirCoeffs {
513 objective_uv,
514 coeff_mm_base,
515 coeff_mm_u,
516 coeff_mm_v,
517 coeff_mm_uv,
518 coeff_ml_base,
519 coeff_ml_u,
520 coeff_ml_v,
521 coeff_ml_uv,
522 coeff_ll_base,
523 coeff_ll_u,
524 coeff_ll_v,
525 coeff_ll_uv,
526 mean_wiggle_base,
527 a_u,
528 a_v,
529 a_uv,
530 c_u,
531 c_v,
532 c_uv,
533 gradient_ls_base: base.gradient_ls.clone(),
534 gradient_ls_u: first_u.gradient_ls.clone(),
535 gradient_ls_v: first_v.gradient_ls.clone(),
536 gradient_ls_uv: second_uv.gradient_ls.clone(),
537 l_u,
538 l_v,
539 l_uv,
540 hessian_mm_base: base.hessian_mm.clone(),
541 gradient_mu_base: base.gradient_mu.clone(),
542 hessian_ml_base: base.hessian_ml.clone(),
543 hessian_mm_u: first_u.hessian_mm.clone(),
544 hessian_mm_v: first_v.hessian_mm.clone(),
545 hessian_mm_uv: second_uv.hessian_mm.clone(),
546 }
547}
548
549impl GaussianLocationScaleWiggleFamily {
550 pub(crate) fn exact_newton_joint_hessian_for_specs(
551 &self,
552 block_states: &[ParameterBlockState],
553 specs: Option<&[ParameterBlockSpec]>,
554 ) -> Result<Option<Array2<f64>>, String> {
555 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(specs)? else {
556 return Ok(None);
557 };
558 self.exact_newton_joint_hessian_from_designs(block_states, &xmu, &x_ls)
559 }
560
561 pub(crate) fn exact_newton_joint_hessian_directional_derivative_for_specs(
562 &self,
563 block_states: &[ParameterBlockState],
564 specs: Option<&[ParameterBlockSpec]>,
565 d_beta_flat: &Array1<f64>,
566 ) -> Result<Option<Array2<f64>>, String> {
567 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(specs)? else {
568 return Ok(None);
569 };
570 self.exact_newton_joint_hessian_directional_derivative_from_designs(
571 block_states,
572 &xmu,
573 &x_ls,
574 d_beta_flat,
575 )
576 }
577
578 pub(crate) fn exact_newton_joint_hessian_second_directional_derivative_for_specs(
579 &self,
580 block_states: &[ParameterBlockState],
581 specs: Option<&[ParameterBlockSpec]>,
582 d_beta_u_flat: &Array1<f64>,
583 d_beta_v_flat: &Array1<f64>,
584 ) -> Result<Option<Array2<f64>>, String> {
585 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(specs)? else {
586 return Ok(None);
587 };
588 self.exact_newton_joint_hessiansecond_directional_derivative_from_designs(
589 block_states,
590 &xmu,
591 &x_ls,
592 d_beta_u_flat,
593 d_beta_v_flat,
594 )
595 }
596
597 pub(crate) fn exact_newton_joint_psi_direction(
598 &self,
599 block_states: &[ParameterBlockState],
600 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
601 psi_index: usize,
602 xmu: &Array2<f64>,
603 x_ls: &Array2<f64>,
604 policy: &gam_runtime::resource::ResourcePolicy,
605 ) -> Result<Option<LocationScaleJointPsiDirection>, String> {
606 let Some(parts) = locscale_joint_psi_direction_parts(
607 block_states,
608 derivative_blocks,
609 psi_index,
610 self.y.len(),
611 xmu.ncols(),
612 x_ls.ncols(),
613 Self::BLOCK_MU,
614 Self::BLOCK_LOG_SIGMA,
615 3,
616 "GaussianLocationScaleWiggleFamily",
617 "mu",
618 policy,
619 )?
620 else {
621 return Ok(None);
622 };
623 Ok(Some(LocationScaleJointPsiDirection {
624 block_idx: parts.block_idx,
625 local_idx: parts.local_idx,
626 z_primary_psi: parts.primary_z,
627 z_ls_psi: parts.log_sigma_z,
628 x_primary_psi: parts.primary_psi,
629 x_ls_psi: parts.log_sigma_psi,
630 }))
631 }
632
633 pub(crate) fn exact_newton_joint_psisecond_design_drifts(
634 &self,
635 block_states: &[ParameterBlockState],
636 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
637 psi_a: &LocationScaleJointPsiDirection,
638 psi_b: &LocationScaleJointPsiDirection,
639 xmu: &Array2<f64>,
640 x_ls: &Array2<f64>,
641 ) -> Result<LocationScaleJointPsiSecondDrifts, String> {
642 locscale_joint_psisecond_design_drifts(
643 block_states,
644 derivative_blocks,
645 psi_a,
646 psi_b,
647 LocScalePsiDriftConfig {
648 n: self.y.len(),
649 p_primary: xmu.ncols(),
650 p_log_sigma: x_ls.ncols(),
651 primary_block_idx: Self::BLOCK_MU,
652 log_sigma_block_idx: Self::BLOCK_LOG_SIGMA,
653 family_name: "GaussianLocationScaleWiggleFamily",
654 primary_label: "mu",
655 policy: &self.policy,
656 },
657 )
658 }
659
660 pub(crate) fn wiggle_hessian_row_pieces(
664 &self,
665 block_states: &[ParameterBlockState],
666 ) -> Result<GaussianLocationScaleWiggleHessianRowPieces, String> {
667 validate_block_count::<GamlssError>(
668 "GaussianLocationScaleWiggleFamily",
669 3,
670 block_states.len(),
671 )?;
672 let q0 = &block_states[Self::BLOCK_MU].eta;
673 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
674 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
675 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
676 let n = self.y.len();
677 if q0.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
678 return Err(GamlssError::DimensionMismatch {
679 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
680 }
681 .into());
682 }
683 let q = q0 + etaw;
684 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
685 if geom.basis.ncols() != betaw.len() {
686 return Err(GamlssError::DimensionMismatch { reason: format!(
687 "GaussianLocationScaleWiggleFamily wiggle basis/beta mismatch: basis has {} columns but beta has {} entries",
688 geom.basis.ncols(),
689 betaw.len()
690 ) }.into());
691 }
692 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
693 let generated = gaussian_row_channels(&rows);
694 let coeff_mm = &generated.hessian_mm * &geom.dq_dq0.mapv(|value| value * value)
695 + &generated.gradient_mu * &geom.d2q_dq02;
696 let coeff_ml = &generated.hessian_ml * &geom.dq_dq0;
697 let coeff_ll = generated.hessian_ll;
698 let coeff_mw_b = &generated.hessian_mm * &geom.dq_dq0;
699 let coeff_mw_d = generated.gradient_mu;
700 let coeff_lw_b = generated.hessian_ml;
701 let coeff_ww = generated.hessian_mm;
702 Ok(GaussianLocationScaleWiggleHessianRowPieces {
703 coeff_mm,
704 coeff_ml,
705 coeff_ll,
706 coeff_mw_b,
707 coeff_mw_d,
708 coeff_lw_b,
709 coeff_ww,
710 basis: geom.basis,
711 basis_d1: geom.basis_d1,
712 })
713 }
714
715 pub(crate) fn exact_newton_joint_hessian_from_designs(
716 &self,
717 block_states: &[ParameterBlockState],
718 xmu: &Array2<f64>,
719 x_ls: &Array2<f64>,
720 ) -> Result<Option<Array2<f64>>, String> {
721 let pieces = self.wiggle_hessian_row_pieces(block_states)?;
722 Ok(Some(pieces.assemble_dense(xmu, x_ls)?))
723 }
724
725 pub(crate) fn exact_newton_joint_hessian_directional_derivative_from_designs(
726 &self,
727 block_states: &[ParameterBlockState],
728 xmu: &Array2<f64>,
729 x_ls: &Array2<f64>,
730 d_beta_flat: &Array1<f64>,
731 ) -> Result<Option<Array2<f64>>, String> {
732 validate_block_count::<GamlssError>(
733 "GaussianLocationScaleWiggleFamily",
734 3,
735 block_states.len(),
736 )?;
737 let pmu = xmu.ncols();
738 let p_ls = x_ls.ncols();
739 let q0 = &block_states[Self::BLOCK_MU].eta;
740 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
741 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
742 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
743 let n = self.y.len();
744 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
745 let (umu, u_ls, uw) = layout.split_three(
746 d_beta_flat,
747 "GaussianLocationScaleWiggleFamily exact joint directional Hessian",
748 )?;
749 if q0.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
750 return Err(GamlssError::DimensionMismatch {
751 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
752 }
753 .into());
754 }
755 let q = q0 + etaw;
756 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
757 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
758 let xi = fast_av(xmu, &umu);
759 let zeta = fast_av(x_ls, &u_ls);
760 let phi = fast_av(&geom.basis, &uw);
761 let mut q_u = &geom.dq_dq0 * ξ
762 q_u += φ
763 let mut s1_u = &geom.d2q_dq02 * ξ
764 s1_u += &fast_av(&geom.basis_d1, &uw);
765 let mut g2_u = &geom.d3q_dq03 * ξ
766 g2_u += &fast_av(&geom.basis_d2, &uw);
767 let basis_u = scale_matrix_rows(&geom.basis_d1, &xi)?;
768 let basis1_u = scale_matrix_rows(&geom.basis_d2, &xi)?;
769 let GlsWiggleFirstDirCoeffs {
770 coeff_mm_u,
771 coeff_ml_u,
772 coeff_ll_u,
773 mean_wiggle_u,
774 gradient_mu_u,
775 scale_wiggle_u,
776 mean_wiggle_base,
777 gradient_mu_base,
778 scale_wiggle_base,
779 hessian_mm_base,
780 hessian_mm_u,
781 ..
782 } = gls_wiggle_first_directional_coeffs(&rows, &geom, &q_u, &zeta, &s1_u, &g2_u);
783
784 let h_mm = xt_diag_x_dense(xmu, &coeff_mm_u)?;
785 let h_ml = xt_diag_y_dense(xmu, &coeff_ml_u, x_ls)?;
786 let h_ll = xt_diag_x_dense(x_ls, &coeff_ll_u)?;
787 let h_mw = xt_diag_y_dense(xmu, &mean_wiggle_u, &geom.basis)?
788 + &xt_diag_y_dense(xmu, &mean_wiggle_base, &basis_u)?
789 + &xt_diag_y_dense(xmu, &gradient_mu_u, &geom.basis_d1)?
790 + &xt_diag_y_dense(xmu, &gradient_mu_base, &basis1_u)?;
791 let h_lw = xt_diag_y_dense(x_ls, &scale_wiggle_u, &geom.basis)?
792 + &xt_diag_y_dense(x_ls, &scale_wiggle_base, &basis_u)?;
793 let a_ww = xt_diag_y_dense(&basis_u, &hessian_mm_base, &geom.basis)?;
794 let h_ww = &a_ww + &a_ww.t() + &xt_diag_x_dense(&geom.basis, &hessian_mm_u)?;
795 Ok(Some(gaussian_pack_wiggle_joint_symmetrichessian(
796 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
797 )))
798 }
799
800 pub(crate) fn gls_wiggle_directional_operator(
804 &self,
805 block_states: &[ParameterBlockState],
806 xmu_arc: Arc<Array2<f64>>,
807 x_ls_arc: Arc<Array2<f64>>,
808 d_beta_flat: &Array1<f64>,
809 ) -> Result<Option<Arc<dyn gam_problem::HyperOperator>>, String> {
810 validate_block_count::<GamlssError>(
811 "GaussianLocationScaleWiggleFamily",
812 3,
813 block_states.len(),
814 )?;
815 let pmu = xmu_arc.ncols();
816 let p_ls = x_ls_arc.ncols();
817 let q0_eta = &block_states[Self::BLOCK_MU].eta;
818 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
819 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
820 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
821 let n = self.y.len();
822 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
823 let (umu, u_ls, uw) =
824 layout.split_three(d_beta_flat, "GLS Wiggle joint dH operator d_beta")?;
825 if q0_eta.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
826 return Err(GamlssError::DimensionMismatch {
827 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
828 }
829 .into());
830 }
831 let q = q0_eta + etaw;
832 let geom = self.wiggle_geometry(q0_eta.view(), betaw.view())?;
833 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
834 let xi = fast_av(xmu_arc.as_ref(), &umu);
835 let zeta = fast_av(x_ls_arc.as_ref(), &u_ls);
836 let phi = fast_av(&geom.basis, &uw);
837 let mut q_u = &geom.dq_dq0 * ξ
838 q_u += φ
839 let mut s1_u = &geom.d2q_dq02 * ξ
840 s1_u += &fast_av(&geom.basis_d1, &uw);
841 let mut g2_u = &geom.d3q_dq03 * ξ
842 g2_u += &fast_av(&geom.basis_d2, &uw);
843 let GlsWiggleFirstDirCoeffs {
844 coeff_mm_u,
845 coeff_ml_u,
846 coeff_ll_u,
847 mean_wiggle_u,
848 gradient_mu_u,
849 scale_wiggle_u,
850 mean_wiggle_base,
851 gradient_mu_base,
852 scale_wiggle_base,
853 hessian_mm_base,
854 hessian_mm_u,
855 ..
856 } = gls_wiggle_first_directional_coeffs(&rows, &geom, &q_u, &zeta, &s1_u, &g2_u);
857
858 let coeff_m_b1 = &(&mean_wiggle_base * &xi) + &gradient_mu_u;
863 let coeff_m_b2 = &gradient_mu_base * ξ
865 let coeff_ls_b1 = &scale_wiggle_base * ξ
868 let coeff_b_b1 = &hessian_mm_base * ξ
872
873 let basis: Arc<Array2<f64>> = Arc::new(geom.basis.clone());
874 let basis_d1: Arc<Array2<f64>> = Arc::new(geom.basis_d1.clone());
875 let basis_d2: Arc<Array2<f64>> = Arc::new(geom.basis_d2.clone());
876 let pw = basis.ncols();
877
878 Ok(Some(Arc::new(RowCoeffOperator::from_directions(
879 vec![pmu, p_ls, pw],
880 vec![
881 (0, xmu_arc),
882 (1, x_ls_arc),
883 (2, basis),
884 (2, basis_d1),
885 (2, basis_d2),
886 ],
887 vec![
888 (0, 0, coeff_mm_u),
890 (0, 1, coeff_ml_u),
892 (1, 1, coeff_ll_u),
894 (0, 2, mean_wiggle_u),
896 (0, 3, coeff_m_b1),
898 (0, 4, coeff_m_b2),
900 (1, 2, scale_wiggle_u),
902 (1, 3, coeff_ls_b1),
904 (2, 2, hessian_mm_u),
906 (2, 3, coeff_b_b1),
908 ],
909 n,
910 ))))
911 }
912
913 pub(crate) fn gls_wiggle_second_directional_operator(
920 &self,
921 block_states: &[ParameterBlockState],
922 xmu_arc: Arc<Array2<f64>>,
923 x_ls_arc: Arc<Array2<f64>>,
924 d_beta_u: &Array1<f64>,
925 d_beta_v: &Array1<f64>,
926 ) -> Result<Option<Arc<dyn gam_problem::HyperOperator>>, String> {
927 validate_block_count::<GamlssError>(
928 "GaussianLocationScaleWiggleFamily",
929 3,
930 block_states.len(),
931 )?;
932 let pmu = xmu_arc.ncols();
933 let p_ls = x_ls_arc.ncols();
934 let q0_eta = &block_states[Self::BLOCK_MU].eta;
935 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
936 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
937 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
938 let n = self.y.len();
939 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
940 let (umu, u_ls, uw) = layout.split_three(d_beta_u, "GLS Wiggle d2H operator (u)")?;
941 let (vmu, v_ls, vw) = layout.split_three(d_beta_v, "GLS Wiggle d2H operator (v)")?;
942 if q0_eta.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
943 return Err(GamlssError::DimensionMismatch {
944 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
945 }
946 .into());
947 }
948 let q = q0_eta + etaw;
949 let geom = self.wiggle_geometry(q0_eta.view(), betaw.view())?;
950 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
951
952 let xi_u = fast_av(xmu_arc.as_ref(), &umu);
953 let xi_v = fast_av(xmu_arc.as_ref(), &vmu);
954 let zeta_u = fast_av(x_ls_arc.as_ref(), &u_ls);
955 let zeta_v = fast_av(x_ls_arc.as_ref(), &v_ls);
956 let phi_u = fast_av(&geom.basis, &uw);
957 let phi_v = fast_av(&geom.basis, &vw);
958 let b1u = fast_av(&geom.basis_d1, &uw);
959 let b1v = fast_av(&geom.basis_d1, &vw);
960 let b2u = fast_av(&geom.basis_d2, &uw);
961 let b2v = fast_av(&geom.basis_d2, &vw);
962 let b3u = fast_av(&geom.basis_d3, &uw);
963 let b3v = fast_av(&geom.basis_d3, &vw);
964
965 let mut q_u = &geom.dq_dq0 * &xi_u;
966 q_u += &phi_u;
967 let mut q_v = &geom.dq_dq0 * &xi_v;
968 q_v += &phi_v;
969 let mut s1_u = &geom.d2q_dq02 * &xi_u;
970 s1_u += &b1u;
971 let mut s1_v = &geom.d2q_dq02 * &xi_v;
972 s1_v += &b1v;
973 let mut g2_u = &geom.d3q_dq03 * &xi_u;
974 g2_u += &b2u;
975 let mut g2_v = &geom.d3q_dq03 * &xi_v;
976 g2_v += &b2v;
977 let q_uv = &(&geom.d2q_dq02 * &(&xi_u * &xi_v)) + &(&b1u * &xi_v) + &(&b1v * &xi_u);
978 let s1_uv = &(&geom.d3q_dq03 * &(&xi_u * &xi_v)) + &(&b2u * &xi_v) + &(&b2v * &xi_u);
979 let g2_uv = &(&geom.d4q_dq04 * &(&xi_u * &xi_v)) + &(&b3u * &xi_v) + &(&b3v * &xi_u);
980 let zeta_uv = Array1::zeros(zeta_u.len());
981
982 let GlsWiggleSecondDirCoeffs {
983 coeff_mm_uv,
984 coeff_ml_uv,
985 coeff_ll_uv,
986 a_u,
987 a_v,
988 a_uv,
989 c_u,
990 c_v,
991 c_uv,
992 l_u,
993 l_v,
994 l_uv,
995 hessian_mm_base,
996 gradient_mu_base,
997 hessian_ml_base,
998 hessian_mm_u,
999 hessian_mm_v,
1000 hessian_mm_uv,
1001 ..
1002 } = gls_wiggle_second_directional_coeffs(
1003 &rows,
1004 &geom,
1005 &GlsWiggleDirPieces {
1006 zeta_u: &zeta_u,
1007 zeta_v: &zeta_v,
1008 zeta_uv: &zeta_uv,
1009 q_u: &q_u,
1010 q_v: &q_v,
1011 q_uv: &q_uv,
1012 s1_u: &s1_u,
1013 s1_v: &s1_v,
1014 s1_uv: &s1_uv,
1015 g2_u: &g2_u,
1016 g2_v: &g2_v,
1017 g2_uv: &g2_uv,
1018 },
1019 );
1020
1021 let xi_u_xi_v = &xi_u * &xi_v;
1023 let coeff_m_b1 = &(&a_u * &xi_v) + &(&a_v * &xi_u) + &c_uv;
1024 let coeff_m_b2 =
1025 &(&hessian_mm_base * &geom.dq_dq0 * &xi_u_xi_v) + &(&c_u * &xi_v) + &(&c_v * &xi_u);
1026 let coeff_m_b3 = &gradient_mu_base * &xi_u_xi_v;
1027 let coeff_ls_b1 = &(&l_u * &xi_v) + &(&l_v * &xi_u);
1031 let coeff_ls_b2 = &hessian_ml_base * &xi_u_xi_v;
1032 let coeff_b_b1 = &(&hessian_mm_u * &xi_v) + &(&hessian_mm_v * &xi_u);
1037 let coeff_b_b2 = &hessian_mm_base * &xi_u_xi_v;
1038 let coeff_b1_b1 = 2.0 * &(&hessian_mm_base * &xi_u_xi_v);
1039
1040 let basis: Arc<Array2<f64>> = Arc::new(geom.basis.clone());
1041 let basis_d1: Arc<Array2<f64>> = Arc::new(geom.basis_d1.clone());
1042 let basis_d2: Arc<Array2<f64>> = Arc::new(geom.basis_d2.clone());
1043 let basis_d3: Arc<Array2<f64>> = Arc::new(geom.basis_d3.clone());
1044 let pw = basis.ncols();
1045
1046 Ok(Some(Arc::new(RowCoeffOperator::from_directions(
1047 vec![pmu, p_ls, pw],
1048 vec![
1049 (0, xmu_arc),
1050 (1, x_ls_arc),
1051 (2, basis),
1052 (2, basis_d1),
1053 (2, basis_d2),
1054 (2, basis_d3),
1055 ],
1056 vec![
1057 (0, 0, coeff_mm_uv),
1059 (0, 1, coeff_ml_uv),
1061 (1, 1, coeff_ll_uv),
1063 (0, 2, a_uv),
1065 (0, 3, coeff_m_b1),
1069 (0, 4, coeff_m_b2),
1074 (0, 5, coeff_m_b3),
1077 (1, 2, l_uv),
1079 (1, 3, coeff_ls_b1),
1083 (1, 4, coeff_ls_b2),
1085 (2, 2, hessian_mm_uv),
1087 (2, 3, coeff_b_b1),
1090 (2, 4, coeff_b_b2),
1092 (3, 3, coeff_b1_b1),
1095 ],
1096 n,
1097 ))))
1098 }
1099
1100 pub(crate) fn exact_newton_joint_hessiansecond_directional_derivative_from_designs(
1101 &self,
1102 block_states: &[ParameterBlockState],
1103 xmu: &Array2<f64>,
1104 x_ls: &Array2<f64>,
1105 d_beta_u_flat: &Array1<f64>,
1106 d_beta_v_flat: &Array1<f64>,
1107 ) -> Result<Option<Array2<f64>>, String> {
1108 validate_block_count::<GamlssError>(
1109 "GaussianLocationScaleWiggleFamily",
1110 3,
1111 block_states.len(),
1112 )?;
1113 let pmu = xmu.ncols();
1114 let p_ls = x_ls.ncols();
1115 let q0 = &block_states[Self::BLOCK_MU].eta;
1116 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
1117 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
1118 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
1119 let n = self.y.len();
1120 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
1121 let (umu, u_ls, uw) = layout.split_three(
1122 d_beta_u_flat,
1123 "GaussianLocationScaleWiggleFamily exact joint second directional Hessian (u)",
1124 )?;
1125 let (vmu, v_ls, vw) = layout.split_three(
1126 d_beta_v_flat,
1127 "GaussianLocationScaleWiggleFamily exact joint second directional Hessian (v)",
1128 )?;
1129 if q0.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
1130 return Err(GamlssError::DimensionMismatch {
1131 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
1132 }
1133 .into());
1134 }
1135 let q = q0 + etaw;
1136 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
1137 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
1138
1139 let xi_u = fast_av(xmu, &umu);
1140 let xi_v = fast_av(xmu, &vmu);
1141 let zeta_u = fast_av(x_ls, &u_ls);
1142 let zeta_v = fast_av(x_ls, &v_ls);
1143 let phi_u = fast_av(&geom.basis, &uw);
1144 let phi_v = fast_av(&geom.basis, &vw);
1145 let b1u = fast_av(&geom.basis_d1, &uw);
1146 let b1v = fast_av(&geom.basis_d1, &vw);
1147 let b2u = fast_av(&geom.basis_d2, &uw);
1148 let b2v = fast_av(&geom.basis_d2, &vw);
1149 let b3u = fast_av(&geom.basis_d3, &uw);
1150 let b3v = fast_av(&geom.basis_d3, &vw);
1151
1152 let mut q_u = &geom.dq_dq0 * &xi_u;
1153 q_u += &phi_u;
1154 let mut q_v = &geom.dq_dq0 * &xi_v;
1155 q_v += &phi_v;
1156 let mut s1_u = &geom.d2q_dq02 * &xi_u;
1157 s1_u += &b1u;
1158 let mut s1_v = &geom.d2q_dq02 * &xi_v;
1159 s1_v += &b1v;
1160 let mut g2_u = &geom.d3q_dq03 * &xi_u;
1161 g2_u += &b2u;
1162 let mut g2_v = &geom.d3q_dq03 * &xi_v;
1163 g2_v += &b2v;
1164 let q_uv = &(&geom.d2q_dq02 * &(&xi_u * &xi_v)) + &(&b1u * &xi_v) + &(&b1v * &xi_u);
1165 let s1_uv = &(&geom.d3q_dq03 * &(&xi_u * &xi_v)) + &(&b2u * &xi_v) + &(&b2v * &xi_u);
1166 let g2_uv = &(&geom.d4q_dq04 * &(&xi_u * &xi_v)) + &(&b3u * &xi_v) + &(&b3v * &xi_u);
1167 let zeta_uv = Array1::zeros(zeta_u.len());
1168
1169 let basis_u = scale_matrix_rows(&geom.basis_d1, &xi_u)?;
1170 let basis_v = scale_matrix_rows(&geom.basis_d1, &xi_v)?;
1171 let basis_uv = scale_matrix_rows(&geom.basis_d2, &(&xi_u * &xi_v))?;
1172 let basis1_u = scale_matrix_rows(&geom.basis_d2, &xi_u)?;
1173 let basis1_v = scale_matrix_rows(&geom.basis_d2, &xi_v)?;
1174 let basis1_uv = scale_matrix_rows(&geom.basis_d3, &(&xi_u * &xi_v))?;
1175
1176 let GlsWiggleSecondDirCoeffs {
1180 coeff_mm_uv,
1181 coeff_ml_uv,
1182 coeff_ll_uv,
1183 a_u,
1184 a_v,
1185 a_uv,
1186 c_u,
1187 c_v,
1188 c_uv,
1189 l_u,
1190 l_v,
1191 l_uv,
1192 hessian_mm_base,
1193 gradient_mu_base,
1194 hessian_ml_base,
1195 hessian_mm_u,
1196 hessian_mm_v,
1197 hessian_mm_uv,
1198 ..
1199 } = gls_wiggle_second_directional_coeffs(
1200 &rows,
1201 &geom,
1202 &GlsWiggleDirPieces {
1203 zeta_u: &zeta_u,
1204 zeta_v: &zeta_v,
1205 zeta_uv: &zeta_uv,
1206 q_u: &q_u,
1207 q_v: &q_v,
1208 q_uv: &q_uv,
1209 s1_u: &s1_u,
1210 s1_v: &s1_v,
1211 s1_uv: &s1_uv,
1212 g2_u: &g2_u,
1213 g2_v: &g2_v,
1214 g2_uv: &g2_uv,
1215 },
1216 );
1217
1218 let h_mm = xt_diag_x_dense(xmu, &coeff_mm_uv)?;
1219 let h_ml = xt_diag_y_dense(xmu, &coeff_ml_uv, x_ls)?;
1220 let h_ll = xt_diag_x_dense(x_ls, &coeff_ll_uv)?;
1221 let h_mw = xt_diag_y_dense(xmu, &a_uv, &geom.basis)?
1222 + &xt_diag_y_dense(xmu, &a_u, &basis_v)?
1223 + &xt_diag_y_dense(xmu, &a_v, &basis_u)?
1224 + &xt_diag_y_dense(xmu, &(&hessian_mm_base * &geom.dq_dq0), &basis_uv)?
1225 + &xt_diag_y_dense(xmu, &c_uv, &geom.basis_d1)?
1226 + &xt_diag_y_dense(xmu, &c_u, &basis1_v)?
1227 + &xt_diag_y_dense(xmu, &c_v, &basis1_u)?
1228 + &xt_diag_y_dense(xmu, &gradient_mu_base, &basis1_uv)?;
1229 let h_lw = xt_diag_y_dense(x_ls, &l_uv, &geom.basis)?
1230 + &xt_diag_y_dense(x_ls, &l_u, &basis_v)?
1231 + &xt_diag_y_dense(x_ls, &l_v, &basis_u)?
1232 + &xt_diag_y_dense(x_ls, &hessian_ml_base, &basis_uv)?;
1233 let a_ab = xt_diag_y_dense(&basis_uv, &hessian_mm_base, &geom.basis)?;
1234 let a_ij = xt_diag_y_dense(&basis_u, &hessian_mm_base, &basis_v)?;
1235 let a_iwj = xt_diag_y_dense(&basis_u, &hessian_mm_v, &geom.basis)?;
1236 let a_jwi = xt_diag_y_dense(&basis_v, &hessian_mm_u, &geom.basis)?;
1237 let h_ww = &a_ab
1238 + &a_ab.t()
1239 + &a_ij
1240 + a_ij.t()
1241 + &a_iwj
1242 + a_iwj.t()
1243 + &a_jwi
1244 + a_jwi.t()
1245 + &xt_diag_x_dense(&geom.basis, &hessian_mm_uv)?;
1246 Ok(Some(gaussian_pack_wiggle_joint_symmetrichessian(
1247 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
1248 )))
1249 }
1250
1251 pub(crate) fn exact_newton_joint_psi_terms_from_designs(
1252 &self,
1253 block_states: &[ParameterBlockState],
1254 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1255 psi_index: usize,
1256 xmu: &Array2<f64>,
1257 x_ls: &Array2<f64>,
1258 ) -> Result<Option<gam_problem::ExactNewtonJointPsiTerms>, String> {
1259 let Some(dir_a) = self.exact_newton_joint_psi_direction(
1260 block_states,
1261 derivative_blocks,
1262 psi_index,
1263 xmu,
1264 x_ls,
1265 &self.policy,
1266 )?
1267 else {
1268 return Ok(None);
1269 };
1270 let q0 = &block_states[Self::BLOCK_MU].eta;
1271 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
1272 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
1273 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
1274 let q = q0 + etaw;
1275 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
1276 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
1277 let xmu_map = dir_a.x_primary_psi.as_linear_map_ref();
1278 let x_ls_map = dir_a.x_ls_psi.as_linear_map_ref();
1279
1280 let q_a = &geom.dq_dq0 * &dir_a.z_primary_psi;
1281 let s1_a = &geom.d2q_dq02 * &dir_a.z_primary_psi;
1282 let g2_a = &geom.d3q_dq03 * &dir_a.z_primary_psi;
1283 let basis_a = scale_matrix_rows(&geom.basis_d1, &dir_a.z_primary_psi)?;
1284 let basis1_a = scale_matrix_rows(&geom.basis_d2, &dir_a.z_primary_psi)?;
1285 let e_a = &dir_a.z_ls_psi;
1286 let GlsWiggleFirstDirCoeffs {
1290 coeff_mm_base: coeff_mm,
1291 coeff_ml_base: coeff_ml,
1292 coeff_ll_base: coeff_ll,
1293 coeff_mm_u: coeff_mm_a,
1294 coeff_ml_u: coeff_ml_a,
1295 coeff_ll_u: coeff_ll_a,
1296 mean_wiggle_u: a_a,
1297 gradient_mu_u: c_a,
1298 scale_wiggle_u: l_a,
1299 mean_wiggle_base: a,
1300 gradient_mu_base: c,
1301 gradient_ls_base: s_ls,
1302 gradient_ls_u: s_ls_a,
1303 scale_wiggle_base: l,
1304 hessian_mm_base,
1305 hessian_mm_u,
1306 } = gls_wiggle_first_directional_coeffs(&rows, &geom, &q_a, e_a, &s1_a, &g2_a);
1307 let s_mu = &c * &geom.dq_dq0;
1308 let s_mu_a = &c_a * &geom.dq_dq0 + &c * &s1_a;
1309
1310 let objective_psi = (&c * &q_a + &s_ls * e_a).sum();
1311 let score_psi = gaussian_pack_wiggle_joint_score(
1312 &(xmu_map.transpose_mul(s_mu.view()) + fast_atv(xmu, &s_mu_a)),
1313 &(x_ls_map.transpose_mul(s_ls.view()) + fast_atv(x_ls, &s_ls_a)),
1314 &(fast_atv(&basis_a, &c) + fast_atv(&geom.basis, &c_a)),
1315 );
1316 let h_mm_a1 = weighted_crossprod_psi_maps(
1317 xmu_map,
1318 coeff_mm.view(),
1319 CustomFamilyPsiLinearMapRef::Dense(xmu),
1320 )?;
1321 let h_mm = &h_mm_a1 + &h_mm_a1.t() + &xt_diag_x_dense(xmu, &coeff_mm_a)?;
1322 let h_ml = weighted_crossprod_psi_maps(
1323 xmu_map,
1324 coeff_ml.view(),
1325 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1326 )? + &weighted_crossprod_psi_maps(
1327 CustomFamilyPsiLinearMapRef::Dense(xmu),
1328 coeff_ml.view(),
1329 x_ls_map,
1330 )? + &xt_diag_y_dense(xmu, &coeff_ml_a, x_ls)?;
1331 let h_ll_a1 = weighted_crossprod_psi_maps(
1332 x_ls_map,
1333 coeff_ll.view(),
1334 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1335 )?;
1336 let h_ll = &h_ll_a1 + &h_ll_a1.t() + &xt_diag_x_dense(x_ls, &coeff_ll_a)?;
1337 let h_mw = weighted_crossprod_psi_maps(
1338 xmu_map,
1339 a.view(),
1340 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1341 )? + &xt_diag_y_dense(xmu, &a_a, &geom.basis)?
1342 + &xt_diag_y_dense(xmu, &a, &basis_a)?
1343 + &weighted_crossprod_psi_maps(
1344 xmu_map,
1345 c.view(),
1346 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1347 )?
1348 + &xt_diag_y_dense(xmu, &c_a, &geom.basis_d1)?
1349 + &xt_diag_y_dense(xmu, &c, &basis1_a)?;
1350 let h_lw = weighted_crossprod_psi_maps(
1351 x_ls_map,
1352 l.view(),
1353 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1354 )? + &xt_diag_y_dense(x_ls, &l_a, &geom.basis)?
1355 + &xt_diag_y_dense(x_ls, &l, &basis_a)?;
1356 let h_ww_a1 = xt_diag_y_dense(&basis_a, &hessian_mm_base, &geom.basis)?;
1357 let h_ww = &h_ww_a1 + &h_ww_a1.t() + &xt_diag_x_dense(&geom.basis, &hessian_mm_u)?;
1358
1359 Ok(Some(gam_problem::ExactNewtonJointPsiTerms {
1360 objective_psi,
1361 score_psi,
1362 hessian_psi: gaussian_pack_wiggle_joint_symmetrichessian(
1363 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
1364 ),
1365 hessian_psi_operator: None,
1366 }))
1367 }
1368
1369 pub(crate) fn exact_newton_joint_psisecond_order_terms_from_designs(
1370 &self,
1371 block_states: &[ParameterBlockState],
1372 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1373 psi_i: usize,
1374 psi_j: usize,
1375 xmu: &Array2<f64>,
1376 x_ls: &Array2<f64>,
1377 ) -> Result<Option<gam_problem::ExactNewtonJointPsiSecondOrderTerms>, String> {
1378 let Some(dir_a) = self.exact_newton_joint_psi_direction(
1379 block_states,
1380 derivative_blocks,
1381 psi_i,
1382 xmu,
1383 x_ls,
1384 &self.policy,
1385 )?
1386 else {
1387 return Ok(None);
1388 };
1389 let Some(dir_b) = self.exact_newton_joint_psi_direction(
1390 block_states,
1391 derivative_blocks,
1392 psi_j,
1393 xmu,
1394 x_ls,
1395 &self.policy,
1396 )?
1397 else {
1398 return Ok(None);
1399 };
1400 Ok(Some(
1401 self.exact_newton_joint_psisecond_order_terms_from_parts(
1402 block_states,
1403 derivative_blocks,
1404 &dir_a,
1405 &dir_b,
1406 xmu,
1407 x_ls,
1408 )?,
1409 ))
1410 }
1411
1412 pub(crate) fn exact_newton_joint_psisecond_order_terms_from_parts(
1413 &self,
1414 block_states: &[ParameterBlockState],
1415 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1416 dir_a: &LocationScaleJointPsiDirection,
1417 dir_b: &LocationScaleJointPsiDirection,
1418 xmu: &Array2<f64>,
1419 x_ls: &Array2<f64>,
1420 ) -> Result<gam_problem::ExactNewtonJointPsiSecondOrderTerms, String> {
1421 let second_drifts = self.exact_newton_joint_psisecond_design_drifts(
1422 block_states,
1423 derivative_blocks,
1424 dir_a,
1425 dir_b,
1426 xmu,
1427 x_ls,
1428 )?;
1429 let n = self.y.len();
1430 let xmu_a_map = dir_a.x_primary_psi.as_linear_map_ref();
1431 let x_ls_a_map = dir_a.x_ls_psi.as_linear_map_ref();
1432 let xmu_b_map = dir_b.x_primary_psi.as_linear_map_ref();
1433 let x_ls_b_map = dir_b.x_ls_psi.as_linear_map_ref();
1434 let xmu_ab_map = second_psi_linear_map(
1435 second_drifts.x_primary_ab_action.as_ref(),
1436 second_drifts.x_primary_ab.as_ref(),
1437 n,
1438 xmu.ncols(),
1439 );
1440 let x_ls_ab_map = second_psi_linear_map(
1441 second_drifts.x_ls_ab_action.as_ref(),
1442 second_drifts.x_ls_ab.as_ref(),
1443 n,
1444 x_ls.ncols(),
1445 );
1446 let q0 = &block_states[Self::BLOCK_MU].eta;
1447 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
1448 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
1449 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
1450 let q = q0 + etaw;
1451 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
1452 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
1453
1454 let q_a = &geom.dq_dq0 * &dir_a.z_primary_psi;
1455 let q_b = &geom.dq_dq0 * &dir_b.z_primary_psi;
1456 let q_ab = &(&geom.dq_dq0 * &second_drifts.z_primary_ab)
1457 + &(&geom.d2q_dq02 * &(&dir_a.z_primary_psi * &dir_b.z_primary_psi));
1458 let s1_a = &geom.d2q_dq02 * &dir_a.z_primary_psi;
1459 let s1_b = &geom.d2q_dq02 * &dir_b.z_primary_psi;
1460 let s1_ab = &(&geom.d3q_dq03 * &(&dir_a.z_primary_psi * &dir_b.z_primary_psi))
1461 + &(&geom.d2q_dq02 * &second_drifts.z_primary_ab);
1462 let g2_a = &geom.d3q_dq03 * &dir_a.z_primary_psi;
1463 let g2_b = &geom.d3q_dq03 * &dir_b.z_primary_psi;
1464 let g2_ab = &(&geom.d4q_dq04 * &(&dir_a.z_primary_psi * &dir_b.z_primary_psi))
1465 + &(&geom.d3q_dq03 * &second_drifts.z_primary_ab);
1466 let basis_a = scale_matrix_rows(&geom.basis_d1, &dir_a.z_primary_psi)?;
1467 let basis_b = scale_matrix_rows(&geom.basis_d1, &dir_b.z_primary_psi)?;
1468 let basis_ab = scale_matrix_rows(&geom.basis_d1, &second_drifts.z_primary_ab)?
1469 + &scale_matrix_rows(
1470 &geom.basis_d2,
1471 &(&dir_a.z_primary_psi * &dir_b.z_primary_psi),
1472 )?;
1473 let basis1_a = scale_matrix_rows(&geom.basis_d2, &dir_a.z_primary_psi)?;
1474 let basis1_b = scale_matrix_rows(&geom.basis_d2, &dir_b.z_primary_psi)?;
1475 let basis1_ab = scale_matrix_rows(&geom.basis_d2, &second_drifts.z_primary_ab)?
1476 + &scale_matrix_rows(
1477 &geom.basis_d3,
1478 &(&dir_a.z_primary_psi * &dir_b.z_primary_psi),
1479 )?;
1480
1481 let e_a = &dir_a.z_ls_psi;
1482 let e_b = &dir_b.z_ls_psi;
1483 let e_ab = &second_drifts.z_ls_ab;
1484 let GlsWiggleSecondDirCoeffs {
1485 objective_uv,
1486 coeff_mm_base: coeff_mm,
1487 coeff_mm_u: coeff_mm_a,
1488 coeff_mm_v: coeff_mm_b,
1489 coeff_mm_uv: coeff_mm_ab,
1490 coeff_ml_base: coeff_ml,
1491 coeff_ml_u: coeff_ml_a,
1492 coeff_ml_v: coeff_ml_b,
1493 coeff_ml_uv: coeff_ml_ab,
1494 coeff_ll_base: coeff_ll,
1495 coeff_ll_u: coeff_ll_a,
1496 coeff_ll_v: coeff_ll_b,
1497 coeff_ll_uv: coeff_ll_ab,
1498 mean_wiggle_base: a,
1499 a_u: a_a,
1500 a_v: a_b,
1501 a_uv: a_ab,
1502 gradient_mu_base: c,
1503 c_u: c_a,
1504 c_v: c_b,
1505 c_uv: c_ab,
1506 gradient_ls_base: s_ls,
1507 gradient_ls_u: s_ls_a,
1508 gradient_ls_v: s_ls_b,
1509 gradient_ls_uv: s_ls_ab,
1510 hessian_ml_base: l,
1511 l_u: l_a,
1512 l_v: l_b,
1513 l_uv: l_ab,
1514 hessian_mm_base,
1515 hessian_mm_u: hessian_mm_a,
1516 hessian_mm_v: hessian_mm_b,
1517 hessian_mm_uv: hessian_mm_ab,
1518 } = gls_wiggle_second_directional_coeffs(
1519 &rows,
1520 &geom,
1521 &GlsWiggleDirPieces {
1522 zeta_u: e_a,
1523 zeta_v: e_b,
1524 zeta_uv: e_ab,
1525 q_u: &q_a,
1526 q_v: &q_b,
1527 q_uv: &q_ab,
1528 s1_u: &s1_a,
1529 s1_v: &s1_b,
1530 s1_uv: &s1_ab,
1531 g2_u: &g2_a,
1532 g2_v: &g2_b,
1533 g2_uv: &g2_ab,
1534 },
1535 );
1536 let s_mu = &c * &geom.dq_dq0;
1537 let s_mu_a = &c_a * &geom.dq_dq0 + &c * &s1_a;
1538 let s_mu_b = &c_b * &geom.dq_dq0 + &c * &s1_b;
1539 let s_mu_ab = &c_ab * &geom.dq_dq0 + &c_a * &s1_b + &c_b * &s1_a + &c * &s1_ab;
1540
1541 let objective_psi_psi = objective_uv.sum();
1542
1543 let score_psi_psi = gaussian_pack_wiggle_joint_score(
1544 &(xmu_ab_map.transpose_mul(s_mu.view())
1545 + xmu_a_map.transpose_mul(s_mu_b.view())
1546 + xmu_b_map.transpose_mul(s_mu_a.view())
1547 + fast_atv(xmu, &s_mu_ab)),
1548 &(x_ls_ab_map.transpose_mul(s_ls.view())
1549 + x_ls_a_map.transpose_mul(s_ls_b.view())
1550 + x_ls_b_map.transpose_mul(s_ls_a.view())
1551 + fast_atv(x_ls, &s_ls_ab)),
1552 &(fast_atv(&basis_ab, &c)
1553 + fast_atv(&basis_a, &c_b)
1554 + fast_atv(&basis_b, &c_a)
1555 + fast_atv(&geom.basis, &c_ab)),
1556 );
1557
1558 let hmm_ab = weighted_crossprod_psi_maps(
1559 xmu_ab_map,
1560 coeff_mm.view(),
1561 CustomFamilyPsiLinearMapRef::Dense(xmu),
1562 )?;
1563 let hmm_ij = weighted_crossprod_psi_maps(xmu_a_map, coeff_mm.view(), xmu_b_map)?;
1564 let hmm_iwj = weighted_crossprod_psi_maps(
1565 xmu_a_map,
1566 coeff_mm_b.view(),
1567 CustomFamilyPsiLinearMapRef::Dense(xmu),
1568 )?;
1569 let hmm_jwi = weighted_crossprod_psi_maps(
1570 xmu_b_map,
1571 coeff_mm_a.view(),
1572 CustomFamilyPsiLinearMapRef::Dense(xmu),
1573 )?;
1574 let h_mm = &hmm_ab
1575 + &hmm_ab.t()
1576 + &hmm_ij
1577 + hmm_ij.t()
1578 + &hmm_iwj
1579 + hmm_iwj.t()
1580 + &hmm_jwi
1581 + hmm_jwi.t()
1582 + &xt_diag_x_dense(xmu, &coeff_mm_ab)?;
1583 let h_ml = weighted_crossprod_psi_maps(
1584 xmu_ab_map,
1585 coeff_ml.view(),
1586 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1587 )? + &weighted_crossprod_psi_maps(xmu_a_map, coeff_ml.view(), x_ls_b_map)?
1588 + &weighted_crossprod_psi_maps(xmu_b_map, coeff_ml.view(), x_ls_a_map)?
1589 + &weighted_crossprod_psi_maps(
1590 xmu_a_map,
1591 coeff_ml_b.view(),
1592 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1593 )?
1594 + &weighted_crossprod_psi_maps(
1595 xmu_b_map,
1596 coeff_ml_a.view(),
1597 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1598 )?
1599 + &weighted_crossprod_psi_maps(
1600 CustomFamilyPsiLinearMapRef::Dense(xmu),
1601 coeff_ml_a.view(),
1602 x_ls_b_map,
1603 )?
1604 + &weighted_crossprod_psi_maps(
1605 CustomFamilyPsiLinearMapRef::Dense(xmu),
1606 coeff_ml_b.view(),
1607 x_ls_a_map,
1608 )?
1609 + &xt_diag_y_dense(xmu, &coeff_ml_ab, x_ls)?
1610 + &weighted_crossprod_psi_maps(
1611 CustomFamilyPsiLinearMapRef::Dense(xmu),
1612 coeff_ml.view(),
1613 x_ls_ab_map,
1614 )?;
1615 let hll_ab = weighted_crossprod_psi_maps(
1616 x_ls_ab_map,
1617 coeff_ll.view(),
1618 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1619 )?;
1620 let hll_ij = weighted_crossprod_psi_maps(x_ls_a_map, coeff_ll.view(), x_ls_b_map)?;
1621 let hll_iwj = weighted_crossprod_psi_maps(
1622 x_ls_a_map,
1623 coeff_ll_b.view(),
1624 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1625 )?;
1626 let hll_jwi = weighted_crossprod_psi_maps(
1627 x_ls_b_map,
1628 coeff_ll_a.view(),
1629 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1630 )?;
1631 let h_ll = &hll_ab
1632 + &hll_ab.t()
1633 + &hll_ij
1634 + hll_ij.t()
1635 + &hll_iwj
1636 + hll_iwj.t()
1637 + &hll_jwi
1638 + hll_jwi.t()
1639 + &xt_diag_x_dense(x_ls, &coeff_ll_ab)?;
1640 let h_mw = weighted_crossprod_psi_maps(
1641 xmu_ab_map,
1642 a.view(),
1643 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1644 )? + &weighted_crossprod_psi_maps(
1645 xmu_a_map,
1646 a_b.view(),
1647 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1648 )? + &weighted_crossprod_psi_maps(
1649 xmu_a_map,
1650 a.view(),
1651 CustomFamilyPsiLinearMapRef::Dense(&basis_b),
1652 )? + &weighted_crossprod_psi_maps(
1653 xmu_b_map,
1654 a_a.view(),
1655 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1656 )? + &xt_diag_y_dense(xmu, &a_ab, &geom.basis)?
1657 + &xt_diag_y_dense(xmu, &a_a, &basis_b)?
1658 + &weighted_crossprod_psi_maps(
1659 xmu_b_map,
1660 a.view(),
1661 CustomFamilyPsiLinearMapRef::Dense(&basis_a),
1662 )?
1663 + &xt_diag_y_dense(xmu, &a_b, &basis_a)?
1664 + &xt_diag_y_dense(xmu, &a, &basis_ab)?
1665 + &weighted_crossprod_psi_maps(
1666 xmu_ab_map,
1667 c.view(),
1668 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1669 )?
1670 + &weighted_crossprod_psi_maps(
1671 xmu_a_map,
1672 c_b.view(),
1673 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1674 )?
1675 + &weighted_crossprod_psi_maps(
1676 xmu_a_map,
1677 c.view(),
1678 CustomFamilyPsiLinearMapRef::Dense(&basis1_b),
1679 )?
1680 + &weighted_crossprod_psi_maps(
1681 xmu_b_map,
1682 c_a.view(),
1683 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1684 )?
1685 + &xt_diag_y_dense(xmu, &c_ab, &geom.basis_d1)?
1686 + &xt_diag_y_dense(xmu, &c_a, &basis1_b)?
1687 + &weighted_crossprod_psi_maps(
1688 xmu_b_map,
1689 c.view(),
1690 CustomFamilyPsiLinearMapRef::Dense(&basis1_a),
1691 )?
1692 + &xt_diag_y_dense(xmu, &c_b, &basis1_a)?
1693 + &xt_diag_y_dense(xmu, &c, &basis1_ab)?;
1694 let h_lw = weighted_crossprod_psi_maps(
1695 x_ls_ab_map,
1696 l.view(),
1697 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1698 )? + &weighted_crossprod_psi_maps(
1699 x_ls_a_map,
1700 l_b.view(),
1701 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1702 )? + &weighted_crossprod_psi_maps(
1703 x_ls_a_map,
1704 l.view(),
1705 CustomFamilyPsiLinearMapRef::Dense(&basis_b),
1706 )? + &weighted_crossprod_psi_maps(
1707 x_ls_b_map,
1708 l_a.view(),
1709 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1710 )? + &xt_diag_y_dense(x_ls, &l_ab, &geom.basis)?
1711 + &xt_diag_y_dense(x_ls, &l_a, &basis_b)?
1712 + &weighted_crossprod_psi_maps(
1713 x_ls_b_map,
1714 l.view(),
1715 CustomFamilyPsiLinearMapRef::Dense(&basis_a),
1716 )?
1717 + &xt_diag_y_dense(x_ls, &l_b, &basis_a)?
1718 + &xt_diag_y_dense(x_ls, &l, &basis_ab)?;
1719 let hww_ab = xt_diag_y_dense(&basis_ab, &hessian_mm_base, &geom.basis)?;
1720 let hww_ij = xt_diag_y_dense(&basis_a, &hessian_mm_base, &basis_b)?;
1721 let hww_iwj = xt_diag_y_dense(&basis_a, &hessian_mm_b, &geom.basis)?;
1722 let hww_jwi = xt_diag_y_dense(&basis_b, &hessian_mm_a, &geom.basis)?;
1723 let h_ww = &hww_ab
1724 + &hww_ab.t()
1725 + &hww_ij
1726 + hww_ij.t()
1727 + &hww_iwj
1728 + hww_iwj.t()
1729 + &hww_jwi
1730 + hww_jwi.t()
1731 + &xt_diag_x_dense(&geom.basis, &hessian_mm_ab)?;
1732
1733 Ok(gam_problem::ExactNewtonJointPsiSecondOrderTerms {
1734 objective_psi_psi,
1735 score_psi_psi,
1736 hessian_psi_psi: gaussian_pack_wiggle_joint_symmetrichessian(
1737 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
1738 ),
1739 hessian_psi_psi_operator: None,
1740 })
1741 }
1742
1743 pub(crate) fn exact_newton_joint_psihessian_directional_derivative_from_designs(
1744 &self,
1745 block_states: &[ParameterBlockState],
1746 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1747 psi_index: usize,
1748 d_beta_flat: &Array1<f64>,
1749 xmu: &Array2<f64>,
1750 x_ls: &Array2<f64>,
1751 ) -> Result<Option<Array2<f64>>, String> {
1752 let Some(dir_a) = self.exact_newton_joint_psi_direction(
1753 block_states,
1754 derivative_blocks,
1755 psi_index,
1756 xmu,
1757 x_ls,
1758 &self.policy,
1759 )?
1760 else {
1761 return Ok(None);
1762 };
1763 Ok(Some(
1764 self.exact_newton_joint_psihessian_directional_derivative_from_parts(
1765 block_states,
1766 &dir_a,
1767 d_beta_flat,
1768 xmu,
1769 x_ls,
1770 )?,
1771 ))
1772 }
1773
1774 pub(crate) fn exact_newton_joint_psihessian_directional_derivative_from_parts(
1775 &self,
1776 block_states: &[ParameterBlockState],
1777 dir_a: &LocationScaleJointPsiDirection,
1778 d_beta_flat: &Array1<f64>,
1779 xmu: &Array2<f64>,
1780 x_ls: &Array2<f64>,
1781 ) -> Result<Array2<f64>, String> {
1782 let pmu = xmu.ncols();
1783 let p_ls = x_ls.ncols();
1784 let xmu_map = dir_a.x_primary_psi.as_linear_map_ref();
1785 let x_ls_map = dir_a.x_ls_psi.as_linear_map_ref();
1786 let q0 = &block_states[Self::BLOCK_MU].eta;
1787 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
1788 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
1789 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
1790 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
1791 let (umu, u_ls, uw) = layout.split_three(
1792 d_beta_flat,
1793 "GaussianLocationScaleWiggleFamily joint psi hessian directional derivative",
1794 )?;
1795 let q = q0 + etaw;
1796 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
1797 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
1798
1799 let xi = fast_av(xmu, &umu);
1800 let zeta = fast_av(x_ls, &u_ls);
1801 let zmu_a_u = xmu_map.forward_mul(umu.view());
1802 let zls_a_u = x_ls_map.forward_mul(u_ls.view());
1803 let b1u = fast_av(&geom.basis_d1, &uw);
1804 let b2u = fast_av(&geom.basis_d2, &uw);
1805 let b3u = fast_av(&geom.basis_d3, &uw);
1806
1807 let q_u = &(&geom.dq_dq0 * &xi) + &fast_av(&geom.basis, &uw);
1808 let s1_u = &(&geom.d2q_dq02 * &xi) + &b1u;
1809 let g2_u = &(&geom.d3q_dq03 * &xi) + &b2u;
1810 let g3_u = &(&geom.d4q_dq04 * &xi) + &b3u;
1811
1812 let q_a = &geom.dq_dq0 * &dir_a.z_primary_psi;
1813 let s1_a = &geom.d2q_dq02 * &dir_a.z_primary_psi;
1814 let g2_a = &geom.d3q_dq03 * &dir_a.z_primary_psi;
1815 let q_a_u = &(&s1_u * &dir_a.z_primary_psi) + &(&geom.dq_dq0 * &zmu_a_u);
1816 let s1_a_u = &(&g2_u * &dir_a.z_primary_psi) + &(&geom.d2q_dq02 * &zmu_a_u);
1817 let g2_a_u = &(&g3_u * &dir_a.z_primary_psi) + &(&geom.d3q_dq03 * &zmu_a_u);
1818
1819 let basis_u = scale_matrix_rows(&geom.basis_d1, &xi)?;
1820 let basis1_u = scale_matrix_rows(&geom.basis_d2, &xi)?;
1821 let basis_a = scale_matrix_rows(&geom.basis_d1, &dir_a.z_primary_psi)?;
1822 let basis1_a = scale_matrix_rows(&geom.basis_d2, &dir_a.z_primary_psi)?;
1823 let basis_a_u = scale_matrix_rows(&geom.basis_d2, &(&xi * &dir_a.z_primary_psi))?
1824 + &scale_matrix_rows(&geom.basis_d1, &zmu_a_u)?;
1825 let basis1_a_u = scale_matrix_rows(&geom.basis_d3, &(&xi * &dir_a.z_primary_psi))?
1826 + &scale_matrix_rows(&geom.basis_d2, &zmu_a_u)?;
1827
1828 let e_a = &dir_a.z_ls_psi;
1829 let GlsWiggleSecondDirCoeffs {
1830 coeff_mm_u,
1831 coeff_mm_uv: coeff_mm_a_u,
1832 coeff_ml_u,
1833 coeff_ml_uv: coeff_ml_a_u,
1834 coeff_ll_u,
1835 coeff_ll_uv: coeff_ll_a_u,
1836 mean_wiggle_base: a,
1837 a_u,
1838 a_v: a_a,
1839 a_uv: a_a_u,
1840 gradient_mu_base: c,
1841 c_u,
1842 c_v: c_a,
1843 c_uv: c_a_u,
1844 hessian_ml_base: l,
1845 l_u,
1846 l_v: l_a,
1847 l_uv: l_a_u,
1848 hessian_mm_base,
1849 hessian_mm_u,
1850 hessian_mm_uv: hessian_mm_a_u,
1851 ..
1852 } = gls_wiggle_second_directional_coeffs(
1853 &rows,
1854 &geom,
1855 &GlsWiggleDirPieces {
1856 zeta_u: &zeta,
1857 zeta_v: e_a,
1858 zeta_uv: &zls_a_u,
1859 q_u: &q_u,
1860 q_v: &q_a,
1861 q_uv: &q_a_u,
1862 s1_u: &s1_u,
1863 s1_v: &s1_a,
1864 s1_uv: &s1_a_u,
1865 g2_u: &g2_u,
1866 g2_v: &g2_a,
1867 g2_uv: &g2_a_u,
1868 },
1869 );
1870
1871 let hmm_a1 = weighted_crossprod_psi_maps(
1872 xmu_map,
1873 coeff_mm_u.view(),
1874 CustomFamilyPsiLinearMapRef::Dense(xmu),
1875 )?;
1876 let h_mm = &hmm_a1 + &hmm_a1.t() + &xt_diag_x_dense(xmu, &coeff_mm_a_u)?;
1877 let h_ml = weighted_crossprod_psi_maps(
1878 xmu_map,
1879 coeff_ml_u.view(),
1880 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1881 )? + &weighted_crossprod_psi_maps(
1882 CustomFamilyPsiLinearMapRef::Dense(xmu),
1883 coeff_ml_u.view(),
1884 x_ls_map,
1885 )? + &xt_diag_y_dense(xmu, &coeff_ml_a_u, x_ls)?;
1886 let hll_a1 = weighted_crossprod_psi_maps(
1887 x_ls_map,
1888 coeff_ll_u.view(),
1889 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1890 )?;
1891 let h_ll = &hll_a1 + &hll_a1.t() + &xt_diag_x_dense(x_ls, &coeff_ll_a_u)?;
1892 let h_mw = weighted_crossprod_psi_maps(
1893 xmu_map,
1894 a_u.view(),
1895 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1896 )? + &weighted_crossprod_psi_maps(
1897 xmu_map,
1898 a.view(),
1899 CustomFamilyPsiLinearMapRef::Dense(&basis_u),
1900 )? + &xt_diag_y_dense(xmu, &a_a_u, &geom.basis)?
1901 + &xt_diag_y_dense(xmu, &a_a, &basis_u)?
1902 + &xt_diag_y_dense(xmu, &a_u, &basis_a)?
1903 + &xt_diag_y_dense(xmu, &a, &basis_a_u)?
1904 + &weighted_crossprod_psi_maps(
1905 xmu_map,
1906 c_u.view(),
1907 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1908 )?
1909 + &weighted_crossprod_psi_maps(
1910 xmu_map,
1911 c.view(),
1912 CustomFamilyPsiLinearMapRef::Dense(&basis1_u),
1913 )?
1914 + &xt_diag_y_dense(xmu, &c_a_u, &geom.basis_d1)?
1915 + &xt_diag_y_dense(xmu, &c_a, &basis1_u)?
1916 + &xt_diag_y_dense(xmu, &c_u, &basis1_a)?
1917 + &xt_diag_y_dense(xmu, &c, &basis1_a_u)?;
1918 let h_lw = weighted_crossprod_psi_maps(
1919 x_ls_map,
1920 l_u.view(),
1921 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1922 )? + &weighted_crossprod_psi_maps(
1923 x_ls_map,
1924 l.view(),
1925 CustomFamilyPsiLinearMapRef::Dense(&basis_u),
1926 )? + &xt_diag_y_dense(x_ls, &l_a_u, &geom.basis)?
1927 + &xt_diag_y_dense(x_ls, &l_a, &basis_u)?
1928 + &xt_diag_y_dense(x_ls, &l_u, &basis_a)?
1929 + &xt_diag_y_dense(x_ls, &l, &basis_a_u)?;
1930 let hww_a_u = xt_diag_y_dense(&basis_a_u, &hessian_mm_base, &geom.basis)?;
1931 let hww_aw = xt_diag_y_dense(&basis_a, &hessian_mm_u, &geom.basis)?;
1932 let hww_au = xt_diag_y_dense(&basis_a, &hessian_mm_base, &basis_u)?;
1933 let h_ww = &hww_a_u
1934 + &hww_a_u.t()
1935 + &hww_aw
1936 + hww_aw.t()
1937 + &hww_au
1938 + hww_au.t()
1939 + &xt_diag_x_dense(&geom.basis, &hessian_mm_a_u)?;
1940
1941 Ok(gaussian_pack_wiggle_joint_symmetrichessian(
1942 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
1943 ))
1944 }
1945
1946 pub(crate) fn exact_newton_joint_psi_terms_for_specs(
1947 &self,
1948 block_states: &[ParameterBlockState],
1949 specs: &[ParameterBlockSpec],
1950 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1951 psi_index: usize,
1952 ) -> Result<Option<gam_problem::ExactNewtonJointPsiTerms>, String> {
1953 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
1954 return Ok(None);
1955 };
1956 self.exact_newton_joint_psi_terms_from_designs(
1957 block_states,
1958 derivative_blocks,
1959 psi_index,
1960 &xmu,
1961 &x_ls,
1962 )
1963 }
1964
1965 pub(crate) fn exact_newton_joint_psisecond_order_terms_for_specs(
1966 &self,
1967 block_states: &[ParameterBlockState],
1968 specs: &[ParameterBlockSpec],
1969 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1970 psi_i: usize,
1971 psi_j: usize,
1972 ) -> Result<Option<gam_problem::ExactNewtonJointPsiSecondOrderTerms>, String> {
1973 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
1974 return Ok(None);
1975 };
1976 self.exact_newton_joint_psisecond_order_terms_from_designs(
1977 block_states,
1978 derivative_blocks,
1979 psi_i,
1980 psi_j,
1981 &xmu,
1982 &x_ls,
1983 )
1984 }
1985
1986 pub(crate) fn exact_newton_joint_psihessian_directional_derivative_for_specs(
1987 &self,
1988 block_states: &[ParameterBlockState],
1989 specs: &[ParameterBlockSpec],
1990 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1991 psi_index: usize,
1992 d_beta_flat: &Array1<f64>,
1993 ) -> Result<Option<Array2<f64>>, String> {
1994 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
1995 return Ok(None);
1996 };
1997 self.exact_newton_joint_psihessian_directional_derivative_from_designs(
1998 block_states,
1999 derivative_blocks,
2000 psi_index,
2001 d_beta_flat,
2002 &xmu,
2003 &x_ls,
2004 )
2005 }
2006}
2007
2008impl CustomFamily for GaussianLocationScaleWiggleFamily {
2009 fn joint_jeffreys_term_required(&self) -> bool {
2013 true
2014 }
2015
2016 fn exact_newton_joint_hessian_beta_dependent(&self) -> bool {
2017 true
2018 }
2019
2020 fn outer_seed_config(&self, n_params: usize) -> crate::seeding::SeedConfig {
2028 if n_params == 0 {
2029 return crate::seeding::SeedConfig::default();
2030 }
2031 let mut config = crate::seeding::SeedConfig::default();
2032 config.risk_profile = crate::seeding::SeedRiskProfile::GaussianLocationScale;
2033 config.max_seeds = 4;
2034 config.seed_budget = 2;
2035 config
2036 }
2037
2038 fn coefficient_hessian_cost(&self, specs: &[ParameterBlockSpec]) -> u64 {
2039 crate::location_scale_engine::location_scale_coefficient_hessian_cost(
2044 self.y.len() as u64,
2045 specs,
2046 )
2047 }
2048
2049 fn block_linear_constraints(
2050 &self,
2051 _: &[ParameterBlockState],
2052 block_idx: usize,
2053 spec: &ParameterBlockSpec,
2054 ) -> Result<Option<ConstraintSet>, String> {
2055 if block_idx != Self::BLOCK_WIGGLE {
2056 return Ok(None);
2057 }
2058 Ok(monotone_wiggle_nonnegative_constraints(spec.design.ncols()))
2059 }
2060
2061 fn post_update_block_beta(
2062 &self,
2063 _: &[ParameterBlockState],
2064 block_idx: usize,
2065 block_spec: &ParameterBlockSpec,
2066 beta: Array1<f64>,
2067 ) -> Result<Array1<f64>, String> {
2068 assert!(!block_spec.name.is_empty());
2069 if block_idx != Self::BLOCK_WIGGLE {
2070 return Ok(beta);
2071 }
2072 let beta = project_monotone_wiggle_beta_nonnegative(beta);
2073 validate_monotone_wiggle_beta_nonnegative(
2074 &beta,
2075 "GaussianLocationScaleWiggleFamily post-update",
2076 )?;
2077 Ok(beta)
2078 }
2079
2080 fn evaluate(&self, block_states: &[ParameterBlockState]) -> Result<FamilyEvaluation, String> {
2081 validate_block_count::<GamlssError>(
2082 "GaussianLocationScaleWiggleFamily",
2083 3,
2084 block_states.len(),
2085 )?;
2086 let n = self.y.len();
2087 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2088 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
2089 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
2090 if eta_mu.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
2091 return Err(GamlssError::DimensionMismatch {
2092 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
2093 }
2094 .into());
2095 }
2096 let ln2pi = (2.0 * std::f64::consts::PI).ln();
2097 let certified: Vec<Result<(GaussianDiagonalRowKernel, f64, f64), String>> = (0..n)
2098 .into_par_iter()
2099 .map(|i| {
2100 let q = eta_mu[i] + etaw[i];
2101 if !q.is_finite() {
2102 return Err(GamlssError::RowGeometryUnrepresentable {
2103 row: i,
2104 quantity: "Gaussian mean-plus-wiggle predictor",
2105 eta: eta_mu[i],
2106 value: q,
2107 }
2108 .into());
2109 }
2110 let z_mu = self.y[i] - etaw[i];
2111 let z_wiggle = self.y[i] - eta_mu[i];
2112 if !z_mu.is_finite() || !z_wiggle.is_finite() {
2113 return Err(GamlssError::RowGeometryUnrepresentable {
2114 row: i,
2115 quantity: "Gaussian wiggle working response",
2116 eta: q,
2117 value: if z_mu.is_finite() { z_wiggle } else { z_mu },
2118 }
2119 .into());
2120 }
2121 Ok((
2122 gaussian_diagonal_row_kernel(
2123 i,
2124 self.y[i],
2125 q,
2126 eta_ls[i],
2127 self.weights[i],
2128 ln2pi,
2129 )?,
2130 z_mu,
2131 z_wiggle,
2132 ))
2133 })
2134 .collect();
2135 let mut rows = Vec::with_capacity(n);
2136 for row in certified {
2137 rows.push(row?);
2138 }
2139 let mut ll = 0.0;
2140 for (i, row) in rows.iter().enumerate() {
2141 ll += row.0.log_likelihood;
2142 if !ll.is_finite() {
2143 return Err(GamlssError::RowGeometryUnrepresentable {
2144 row: i,
2145 quantity: "Gaussian wiggle cumulative log likelihood",
2146 eta: eta_ls[i],
2147 value: ll,
2148 }
2149 .into());
2150 }
2151 }
2152 let zmu = Array1::from_iter(rows.iter().map(|row| row.1));
2153 let zw = Array1::from_iter(rows.iter().map(|row| row.2));
2154 let wmu = Array1::from_iter(rows.iter().map(|row| row.0.location_working_weight));
2155 let ww = wmu.clone();
2156 let zls = Array1::from_iter(rows.iter().map(|row| row.0.log_sigma_working_response));
2157 let wls = Array1::from_iter(rows.iter().map(|row| row.0.log_sigma_working_weight));
2158
2159 Ok(FamilyEvaluation {
2160 log_likelihood: ll,
2161 blockworking_sets: vec![
2162 BlockWorkingSet::diagonal_checked(zmu, wmu)?,
2163 BlockWorkingSet::diagonal_checked(zls, wls)?,
2164 BlockWorkingSet::diagonal_checked(zw, ww)?,
2165 ],
2166 })
2167 }
2168
2169 fn log_likelihood_only(&self, block_states: &[ParameterBlockState]) -> Result<f64, String> {
2170 validate_block_count::<GamlssError>(
2171 "GaussianLocationScaleWiggleFamily",
2172 3,
2173 block_states.len(),
2174 )?;
2175 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2176 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
2177 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
2178 if eta_mu.len() != self.y.len()
2179 || eta_ls.len() != self.y.len()
2180 || etaw.len() != self.y.len()
2181 || self.weights.len() != self.y.len()
2182 {
2183 return Err(GamlssError::DimensionMismatch {
2184 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
2185 }
2186 .into());
2187 }
2188 let ln2pi = (2.0 * std::f64::consts::PI).ln();
2189 let mut ll = 0.0;
2190 for i in 0..self.y.len() {
2191 let q = eta_mu[i] + etaw[i];
2192 ll += gaussian_diagonal_row_kernel(i, self.y[i], q, eta_ls[i], self.weights[i], ln2pi)?
2193 .log_likelihood;
2194 if !ll.is_finite() {
2195 return Err(GamlssError::RowGeometryUnrepresentable {
2196 row: i,
2197 quantity: "Gaussian wiggle cumulative log likelihood",
2198 eta: eta_ls[i],
2199 value: ll,
2200 }
2201 .into());
2202 }
2203 }
2204 Ok(ll)
2205 }
2206
2207 fn log_likelihood_only_with_options(
2218 &self,
2219 block_states: &[ParameterBlockState],
2220 options: &BlockwiseFitOptions,
2221 ) -> Result<f64, String> {
2222 let Some(subsample) = options.outer_score_subsample.as_ref() else {
2223 return self.log_likelihood_only(block_states);
2224 };
2225 validate_block_count::<GamlssError>(
2226 "GaussianLocationScaleWiggleFamily",
2227 3,
2228 block_states.len(),
2229 )?;
2230 let n = self.y.len();
2231 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2232 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
2233 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
2234 if eta_mu.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
2235 return Err(GamlssError::DimensionMismatch {
2236 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
2237 }
2238 .into());
2239 }
2240 let ln2pi = (2.0 * std::f64::consts::PI).ln();
2241 let mut ll = 0.0;
2242 for sampled in subsample.rows.iter() {
2243 let i = sampled.index;
2244 let q = eta_mu[i] + etaw[i];
2245 let row_ll =
2246 gaussian_diagonal_row_kernel(i, self.y[i], q, eta_ls[i], self.weights[i], ln2pi)?
2247 .log_likelihood;
2248 let contribution = scaled_signed_product3(sampled.weight, row_ll, 1.0);
2249 ll += contribution;
2250 if !contribution.is_finite() || !ll.is_finite() {
2251 return Err(GamlssError::RowGeometryUnrepresentable {
2252 row: i,
2253 quantity: "Gaussian wiggle subsampled log likelihood",
2254 eta: eta_ls[i],
2255 value: if contribution.is_finite() {
2256 ll
2257 } else {
2258 contribution
2259 },
2260 }
2261 .into());
2262 }
2263 }
2264 Ok(ll)
2265 }
2266
2267 fn requires_joint_outer_hyper_path(&self) -> bool {
2268 true
2269 }
2270
2271 fn exact_newton_hessian_directional_derivative(
2272 &self,
2273 block_states: &[ParameterBlockState],
2274 block_idx: usize,
2275 d_beta: &Array1<f64>,
2276 ) -> Result<Option<Array2<f64>>, String> {
2277 validate_block_count::<GamlssError>(
2278 "GaussianLocationScaleWiggleFamily",
2279 3,
2280 block_states.len(),
2281 )?;
2282 let pmu = self
2283 .mu_design
2284 .as_ref()
2285 .ok_or_else(|| {
2286 "GaussianLocationScaleWiggleFamily exact path is missing mu design".to_string()
2287 })?
2288 .ncols();
2289 let p_ls = self
2290 .log_sigma_design
2291 .as_ref()
2292 .ok_or_else(|| {
2293 "GaussianLocationScaleWiggleFamily exact path is missing log-sigma design"
2294 .to_string()
2295 })?
2296 .ncols();
2297 let pw = block_states[Self::BLOCK_WIGGLE].beta.len();
2298 let total = pmu + p_ls + pw;
2299 let (start, end) = match block_idx {
2300 Self::BLOCK_MU => (0usize, pmu),
2301 Self::BLOCK_LOG_SIGMA => (pmu, pmu + p_ls),
2302 Self::BLOCK_WIGGLE => (pmu + p_ls, total),
2303 _ => return Ok(None),
2304 };
2305 if d_beta.len() != end - start {
2306 return Err(GamlssError::DimensionMismatch { reason: format!(
2307 "GaussianLocationScaleWiggleFamily block {block_idx} d_beta length mismatch: got {}, expected {}",
2308 d_beta.len(),
2309 end - start
2310 ) }.into());
2311 }
2312 let mut d_beta_flat = Array1::<f64>::zeros(total);
2313 d_beta_flat.slice_mut(s![start..end]).assign(d_beta);
2314 let (xmu, x_ls) = self.dense_block_designs()?;
2315 let d_joint = self
2316 .exact_newton_joint_hessian_directional_derivative_from_designs(
2317 block_states,
2318 &xmu,
2319 &x_ls,
2320 &d_beta_flat,
2321 )?
2322 .ok_or_else(|| "missing Gaussian wiggle exact joint directional Hessian".to_string())?;
2323 Ok(Some(d_joint.slice(s![start..end, start..end]).to_owned()))
2324 }
2325
2326 fn exact_newton_joint_hessian(
2327 &self,
2328 block_states: &[ParameterBlockState],
2329 ) -> Result<Option<Array2<f64>>, String> {
2330 self.exact_newton_joint_hessian_for_specs(block_states, None)
2331 }
2332
2333 fn has_explicit_joint_hessian(&self) -> bool {
2334 true
2335 }
2336
2337 fn exact_newton_joint_hessian_directional_derivative(
2338 &self,
2339 block_states: &[ParameterBlockState],
2340 d_beta_flat: &Array1<f64>,
2341 ) -> Result<Option<Array2<f64>>, String> {
2342 self.exact_newton_joint_hessian_directional_derivative_for_specs(
2343 block_states,
2344 None,
2345 d_beta_flat,
2346 )
2347 }
2348
2349 fn exact_newton_joint_hessiansecond_directional_derivative(
2350 &self,
2351 block_states: &[ParameterBlockState],
2352 d_beta_u_flat: &Array1<f64>,
2353 d_beta_v_flat: &Array1<f64>,
2354 ) -> Result<Option<Array2<f64>>, String> {
2355 self.exact_newton_joint_hessian_second_directional_derivative_for_specs(
2356 block_states,
2357 None,
2358 d_beta_u_flat,
2359 d_beta_v_flat,
2360 )
2361 }
2362
2363 fn exact_newton_joint_hessian_with_specs(
2364 &self,
2365 block_states: &[ParameterBlockState],
2366 specs: &[ParameterBlockSpec],
2367 ) -> Result<Option<Array2<f64>>, String> {
2368 self.exact_newton_joint_hessian_for_specs(block_states, Some(specs))
2369 }
2370
2371 fn exact_newton_joint_hessian_directional_derivative_with_specs(
2372 &self,
2373 block_states: &[ParameterBlockState],
2374 specs: &[ParameterBlockSpec],
2375 d_beta_flat: &Array1<f64>,
2376 ) -> Result<Option<Array2<f64>>, String> {
2377 self.exact_newton_joint_hessian_directional_derivative_for_specs(
2378 block_states,
2379 Some(specs),
2380 d_beta_flat,
2381 )
2382 }
2383
2384 fn exact_newton_joint_hessian_second_directional_derivative_with_specs(
2385 &self,
2386 block_states: &[ParameterBlockState],
2387 specs: &[ParameterBlockSpec],
2388 d_beta_u_flat: &Array1<f64>,
2389 d_beta_v_flat: &Array1<f64>,
2390 ) -> Result<Option<Array2<f64>>, String> {
2391 self.exact_newton_joint_hessian_second_directional_derivative_for_specs(
2392 block_states,
2393 Some(specs),
2394 d_beta_u_flat,
2395 d_beta_v_flat,
2396 )
2397 }
2398
2399 fn exact_newton_joint_psi_terms(
2400 &self,
2401 block_states: &[ParameterBlockState],
2402 specs: &[ParameterBlockSpec],
2403 hyper_layout: &crate::custom_family::CustomFamilyHyperLayout,
2404 psi_index: usize,
2405 ) -> Result<Option<gam_problem::ExactNewtonJointPsiTerms>, String> {
2406 if hyper_layout.family_axis_count() != 0 {
2407 return Err("GaussianLocationScaleWiggleFamily does not declare family-owned hyper axes"
2408 .to_string());
2409 }
2410 self.exact_newton_joint_psi_terms_for_specs(
2411 block_states,
2412 specs,
2413 hyper_layout.design_derivative_blocks(),
2414 psi_index,
2415 )
2416 }
2417
2418 fn exact_newton_joint_psisecond_order_terms(
2419 &self,
2420 block_states: &[ParameterBlockState],
2421 specs: &[ParameterBlockSpec],
2422 hyper_layout: &crate::custom_family::CustomFamilyHyperLayout,
2423 psi_i: usize,
2424 psi_j: usize,
2425 ) -> Result<Option<gam_problem::ExactNewtonJointPsiSecondOrderTerms>, String> {
2426 if hyper_layout.family_axis_count() != 0 {
2427 return Err("GaussianLocationScaleWiggleFamily does not declare family-owned hyper axes"
2428 .to_string());
2429 }
2430 self.exact_newton_joint_psisecond_order_terms_for_specs(
2431 block_states,
2432 specs,
2433 hyper_layout.design_derivative_blocks(),
2434 psi_i,
2435 psi_j,
2436 )
2437 }
2438
2439 fn exact_newton_joint_psihessian_directional_derivative(
2440 &self,
2441 block_states: &[ParameterBlockState],
2442 specs: &[ParameterBlockSpec],
2443 hyper_layout: &crate::custom_family::CustomFamilyHyperLayout,
2444 psi_index: usize,
2445 d_beta_flat: &Array1<f64>,
2446 ) -> Result<Option<Array2<f64>>, String> {
2447 if hyper_layout.family_axis_count() != 0 {
2448 return Err("GaussianLocationScaleWiggleFamily does not declare family-owned hyper axes"
2449 .to_string());
2450 }
2451 self.exact_newton_joint_psihessian_directional_derivative_for_specs(
2452 block_states,
2453 specs,
2454 hyper_layout.design_derivative_blocks(),
2455 psi_index,
2456 d_beta_flat,
2457 )
2458 }
2459
2460 fn exact_newton_joint_psi_workspace(
2461 &self,
2462 block_states: &[ParameterBlockState],
2463 specs: &[ParameterBlockSpec],
2464 hyper_layout: &crate::custom_family::CustomFamilyHyperLayout,
2465 ) -> Result<Option<Arc<dyn ExactNewtonJointPsiWorkspace>>, String> {
2466 if hyper_layout.family_axis_count() != 0 {
2467 return Err("GaussianLocationScaleWiggleFamily does not declare family-owned hyper axes"
2468 .to_string());
2469 }
2470 if !self.exact_joint_supported() {
2471 return Ok(None);
2472 }
2473 Ok(Some(Arc::new(
2474 GaussianLocationScaleWiggleExactNewtonJointPsiWorkspace::new(
2475 self.clone(),
2476 block_states.to_vec(),
2477 specs,
2478 hyper_layout.design_derivative_blocks().to_vec(),
2479 )?,
2480 )))
2481 }
2482
2483 fn exact_newton_joint_psi_workspace_with_options(
2500 &self,
2501 block_states: &[ParameterBlockState],
2502 specs: &[ParameterBlockSpec],
2503 hyper_layout: &crate::custom_family::CustomFamilyHyperLayout,
2504 options: &BlockwiseFitOptions,
2505 ) -> Result<Option<Arc<dyn ExactNewtonJointPsiWorkspace>>, String> {
2506 if hyper_layout.family_axis_count() != 0 {
2507 return Err("GaussianLocationScaleWiggleFamily does not declare family-owned hyper axes"
2508 .to_string());
2509 }
2510 if !self.exact_joint_supported() {
2511 return Ok(None);
2512 }
2513 Ok(Some(Arc::new(
2514 GaussianLocationScaleWiggleExactNewtonJointPsiWorkspace::new_with_subsample(
2515 self.clone(),
2516 block_states.to_vec(),
2517 specs,
2518 hyper_layout.design_derivative_blocks().to_vec(),
2519 options.outer_score_subsample.clone(),
2520 )?,
2521 )))
2522 }
2523
2524 fn block_geometry(
2525 &self,
2526 block_states: &[ParameterBlockState],
2527 spec: &ParameterBlockSpec,
2528 ) -> Result<(DesignMatrix, Array1<f64>), String> {
2529 if spec.name != "wiggle" {
2530 return Ok((spec.design.clone(), spec.offset.clone()));
2531 }
2532 if block_states.is_empty() {
2533 return Err(GamlssError::UnsupportedConfiguration {
2534 reason: "Gaussian wiggle geometry requires mean block".to_string(),
2535 }
2536 .into());
2537 }
2538 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2539 if eta_mu.len() != self.y.len() {
2540 return Err(GamlssError::DimensionMismatch {
2541 reason: "Gaussian wiggle geometry input size mismatch".to_string(),
2542 }
2543 .into());
2544 }
2545 let x = self.wiggle_design(eta_mu.view())?;
2546 if x.ncols() != spec.design.ncols() {
2547 return Err(GamlssError::DimensionMismatch {
2548 reason: format!(
2549 "Gaussian dynamic wiggle design col mismatch: got {}, expected {}",
2550 x.ncols(),
2551 spec.design.ncols()
2552 ),
2553 }
2554 .into());
2555 }
2556 let nrows = x.nrows();
2557 Ok((
2558 DesignMatrix::Dense(gam_linalg::matrix::DenseDesignMatrix::from(x)),
2559 Array1::zeros(nrows),
2560 ))
2561 }
2562
2563 fn block_geometry_is_dynamic(&self) -> bool {
2564 true
2565 }
2566
2567 fn exact_newton_joint_hessian_workspace(
2568 &self,
2569 block_states: &[ParameterBlockState],
2570 specs: &[ParameterBlockSpec],
2571 ) -> Result<Option<Arc<dyn ExactNewtonJointHessianWorkspace>>, String> {
2572 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
2573 return Ok(None);
2574 };
2575 let workspace = GaussianLocationScaleWiggleHessianWorkspace::new(
2576 self.clone(),
2577 block_states.to_vec(),
2578 xmu.into_owned(),
2579 x_ls.into_owned(),
2580 )?;
2581 Ok(Some(Arc::new(workspace)))
2582 }
2583
2584 fn exact_newton_joint_hessian_workspace_with_options(
2604 &self,
2605 block_states: &[ParameterBlockState],
2606 specs: &[ParameterBlockSpec],
2607 options: &BlockwiseFitOptions,
2608 ) -> Result<Option<Arc<dyn ExactNewtonJointHessianWorkspace>>, String> {
2609 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
2610 return Ok(None);
2611 };
2612 let mut workspace = GaussianLocationScaleWiggleHessianWorkspace::new(
2613 self.clone(),
2614 block_states.to_vec(),
2615 xmu.into_owned(),
2616 x_ls.into_owned(),
2617 )?;
2618 if let Some(subsample) = options.outer_score_subsample.as_ref() {
2619 workspace.apply_outer_subsample(subsample.rows.as_ref());
2620 }
2621 Ok(Some(Arc::new(workspace)))
2622 }
2623
2624 fn outer_derivative_subsample_capable(&self) -> bool {
2640 true
2641 }
2642
2643 fn inner_coefficient_hessian_hvp_available(&self, specs: &[ParameterBlockSpec]) -> bool {
2644 self.exact_joint_supported()
2651 && matches!(
2652 self.exact_joint_dense_block_designs(Some(specs)),
2653 Ok(Some(_))
2654 )
2655 }
2656}
2657
2658pub(crate) struct GaussianLocationScaleWiggleHessianWorkspace {
2672 pub(crate) family: GaussianLocationScaleWiggleFamily,
2673 pub(crate) block_states: Vec<ParameterBlockState>,
2674 pub(crate) xmu: Arc<Array2<f64>>,
2675 pub(crate) x_ls: Arc<Array2<f64>>,
2676 pub(crate) pieces: GaussianLocationScaleWiggleHessianRowPieces,
2677}
2678
2679impl GaussianLocationScaleWiggleHessianWorkspace {
2680 pub(crate) fn new(
2681 family: GaussianLocationScaleWiggleFamily,
2682 block_states: Vec<ParameterBlockState>,
2683 xmu: Array2<f64>,
2684 x_ls: Array2<f64>,
2685 ) -> Result<Self, String> {
2686 let pieces = family.wiggle_hessian_row_pieces(&block_states)?;
2687 Ok(Self {
2688 family,
2689 block_states,
2690 xmu: Arc::new(xmu),
2691 x_ls: Arc::new(x_ls),
2692 pieces,
2693 })
2694 }
2695
2696 pub(crate) fn apply_outer_subsample(
2711 &mut self,
2712 rows: &[crate::outer_subsample::WeightedOuterRow],
2713 ) {
2714 let n = self.pieces.coeff_mm.len();
2715 let mut mask_mm = Array1::<f64>::zeros(n);
2716 let mut mask_ml = Array1::<f64>::zeros(n);
2717 let mut mask_ll = Array1::<f64>::zeros(n);
2718 let mut mask_mw_b = Array1::<f64>::zeros(n);
2719 let mut mask_mw_d = Array1::<f64>::zeros(n);
2720 let mut mask_lw_b = Array1::<f64>::zeros(n);
2721 let mut maskww = Array1::<f64>::zeros(n);
2722 for r in rows {
2723 let i = r.index;
2724 let w = r.weight;
2725 mask_mm[i] = self.pieces.coeff_mm[i] * w;
2726 mask_ml[i] = self.pieces.coeff_ml[i] * w;
2727 mask_ll[i] = self.pieces.coeff_ll[i] * w;
2728 mask_mw_b[i] = self.pieces.coeff_mw_b[i] * w;
2729 mask_mw_d[i] = self.pieces.coeff_mw_d[i] * w;
2730 mask_lw_b[i] = self.pieces.coeff_lw_b[i] * w;
2731 maskww[i] = self.pieces.coeff_ww[i] * w;
2732 }
2733 self.pieces.coeff_mm = mask_mm;
2734 self.pieces.coeff_ml = mask_ml;
2735 self.pieces.coeff_ll = mask_ll;
2736 self.pieces.coeff_mw_b = mask_mw_b;
2737 self.pieces.coeff_mw_d = mask_mw_d;
2738 self.pieces.coeff_lw_b = mask_lw_b;
2739 self.pieces.coeff_ww = maskww;
2740 }
2741}
2742
2743impl ExactNewtonJointHessianWorkspace for GaussianLocationScaleWiggleHessianWorkspace {
2744 fn warm_up_outer_caches_for_mode(
2745 &self,
2746 eval_mode: gam_problem::EvalMode,
2747 ) -> Result<(), String> {
2748 match eval_mode {
2749 gam_problem::EvalMode::ValueOnly
2750 | gam_problem::EvalMode::ValueAndGradient
2751 | gam_problem::EvalMode::ValueGradientHessian => Ok(()),
2752 }
2753 }
2754
2755 fn hessian_dense(&self) -> Result<Option<Array2<f64>>, String> {
2756 let dense = self
2763 .pieces
2764 .assemble_dense(self.xmu.as_ref(), self.x_ls.as_ref())?;
2765 Ok(Some(dense))
2766 }
2767
2768 fn hessian_matvec_available(&self) -> bool {
2769 true
2770 }
2771
2772 fn hessian_matvec(&self, v: &Array1<f64>) -> Result<Option<Array1<f64>>, String> {
2773 let pmu = self.xmu.ncols();
2774 let p_ls = self.x_ls.ncols();
2775 let pw = self.pieces.basis.ncols();
2776 let total = pmu + p_ls + pw;
2777 if v.len() != total {
2778 return Err(GamlssError::DimensionMismatch {
2779 reason: format!(
2780 "GaussianLocationScaleWiggle matvec dimension mismatch: got {}, expected {}",
2781 v.len(),
2782 total
2783 ),
2784 }
2785 .into());
2786 }
2787 let v_mu = v.slice(s![0..pmu]);
2788 let v_ls = v.slice(s![pmu..pmu + p_ls]);
2789 let v_w = v.slice(s![pmu + p_ls..total]);
2790
2791 let u_mu = fast_av(self.xmu.as_ref(), &v_mu);
2792 let u_ls = fast_av(self.x_ls.as_ref(), &v_ls);
2793 let u_b = fast_av(&self.pieces.basis, &v_w);
2794 let u_d = fast_av(&self.pieces.basis_d1, &v_w);
2795
2796 let r_mu = &self.pieces.coeff_mm * &u_mu
2797 + &self.pieces.coeff_ml * &u_ls
2798 + &self.pieces.coeff_mw_b * &u_b
2799 + &self.pieces.coeff_mw_d * &u_d;
2800 let r_ls = &self.pieces.coeff_ml * &u_mu
2801 + &self.pieces.coeff_ll * &u_ls
2802 + &self.pieces.coeff_lw_b * &u_b;
2803 let r_b = &self.pieces.coeff_mw_b * &u_mu
2804 + &self.pieces.coeff_lw_b * &u_ls
2805 + &self.pieces.coeff_ww * &u_b;
2806 let r_d = &self.pieces.coeff_mw_d * &u_mu;
2807
2808 let out_mu = fast_atv(self.xmu.as_ref(), &r_mu);
2809 let out_ls = fast_atv(self.x_ls.as_ref(), &r_ls);
2810 let out_w = fast_atv(&self.pieces.basis, &r_b) + &fast_atv(&self.pieces.basis_d1, &r_d);
2811
2812 let mut out = Array1::<f64>::zeros(total);
2813 out.slice_mut(s![0..pmu]).assign(&out_mu);
2814 out.slice_mut(s![pmu..pmu + p_ls]).assign(&out_ls);
2815 out.slice_mut(s![pmu + p_ls..total]).assign(&out_w);
2816 Ok(Some(out))
2817 }
2818
2819 fn hessian_diagonal(&self) -> Result<Option<Array1<f64>>, String> {
2820 let pmu = self.xmu.ncols();
2821 let p_ls = self.x_ls.ncols();
2822 let pw = self.pieces.basis.ncols();
2823 let total = pmu + p_ls + pw;
2824 use rayon::iter::{IntoParallelIterator, ParallelIterator};
2826 let diag_mu: Vec<f64> = (0..pmu)
2827 .into_par_iter()
2828 .map(|j| {
2829 let col = self.xmu.column(j);
2830 col.iter()
2831 .zip(self.pieces.coeff_mm.iter())
2832 .map(|(&v, &c)| c * v * v)
2833 .sum()
2834 })
2835 .collect();
2836 let diag_ls: Vec<f64> = (0..p_ls)
2837 .into_par_iter()
2838 .map(|j| {
2839 let col = self.x_ls.column(j);
2840 col.iter()
2841 .zip(self.pieces.coeff_ll.iter())
2842 .map(|(&v, &c)| c * v * v)
2843 .sum()
2844 })
2845 .collect();
2846 let diag_w: Vec<f64> = (0..pw)
2847 .into_par_iter()
2848 .map(|j| {
2849 let col = self.pieces.basis.column(j);
2850 col.iter()
2851 .zip(self.pieces.coeff_ww.iter())
2852 .map(|(&v, &c)| c * v * v)
2853 .sum()
2854 })
2855 .collect();
2856 let mut diag = Array1::<f64>::zeros(total);
2857 for (j, v) in diag_mu.into_iter().enumerate() {
2858 diag[j] = v;
2859 }
2860 for (j, v) in diag_ls.into_iter().enumerate() {
2861 diag[pmu + j] = v;
2862 }
2863 for (j, v) in diag_w.into_iter().enumerate() {
2864 diag[pmu + p_ls + j] = v;
2865 }
2866 Ok(Some(diag))
2867 }
2868
2869 fn directional_derivative(
2870 &self,
2871 d_beta_flat: &Array1<f64>,
2872 ) -> Result<Option<Array2<f64>>, String> {
2873 self.family
2874 .exact_newton_joint_hessian_directional_derivative_from_designs(
2875 &self.block_states,
2876 self.xmu.as_ref(),
2877 self.x_ls.as_ref(),
2878 d_beta_flat,
2879 )
2880 }
2881
2882 fn directional_derivative_operator(
2883 &self,
2884 d_beta_flat: &Array1<f64>,
2885 ) -> Result<Option<Arc<dyn gam_problem::HyperOperator>>, String> {
2886 self.family.gls_wiggle_directional_operator(
2887 &self.block_states,
2888 self.xmu.clone(),
2889 self.x_ls.clone(),
2890 d_beta_flat,
2891 )
2892 }
2893
2894 fn second_directional_derivative(
2895 &self,
2896 d_beta_u_flat: &Array1<f64>,
2897 d_beta_v_flat: &Array1<f64>,
2898 ) -> Result<Option<Array2<f64>>, String> {
2899 self.family
2900 .exact_newton_joint_hessiansecond_directional_derivative_from_designs(
2901 &self.block_states,
2902 self.xmu.as_ref(),
2903 self.x_ls.as_ref(),
2904 d_beta_u_flat,
2905 d_beta_v_flat,
2906 )
2907 }
2908
2909 fn second_directional_derivative_operator(
2910 &self,
2911 d_beta_u: &Array1<f64>,
2912 d_beta_v: &Array1<f64>,
2913 ) -> Result<Option<Arc<dyn gam_problem::HyperOperator>>, String> {
2914 self.family.gls_wiggle_second_directional_operator(
2915 &self.block_states,
2916 self.xmu.clone(),
2917 self.x_ls.clone(),
2918 d_beta_u,
2919 d_beta_v,
2920 )
2921 }
2922}
2923
2924impl CustomFamilyGenerative for GaussianLocationScaleWiggleFamily {
2925 fn generativespec(
2926 &self,
2927 block_states: &[ParameterBlockState],
2928 ) -> Result<GenerativeSpec, String> {
2929 validate_block_count::<GamlssError>(
2930 "GaussianLocationScaleWiggleFamily",
2931 3,
2932 block_states.len(),
2933 )?;
2934 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2935 let eta_wiggle = &block_states[Self::BLOCK_WIGGLE].eta;
2936 let eta_log_sigma = &block_states[Self::BLOCK_LOG_SIGMA].eta;
2937 let n = eta_mu.len();
2938 let mean = gamlss_rowwise_map(n, |i| eta_mu[i] + eta_wiggle[i]);
2939 let sigma = gamlss_rowwise_map(n, |i| logb_sigma_from_eta_scalar(eta_log_sigma[i]));
2940 Ok(GenerativeSpec {
2941 mean,
2942 noise: NoiseModel::Gaussian { sigma },
2943 })
2944 }
2945}
2946
2947pub(crate) fn expect_single_block<'a>(
2948 block_states: &'a [ParameterBlockState],
2949 family_name: &str,
2950) -> Result<&'a ParameterBlockState, String> {
2951 validate_block_count::<GamlssError>(family_name, 1, block_states.len())?;
2952 Ok(&block_states[0])
2953}