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