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) coeff_mm_uv: Array1<f64>,
318 pub(crate) coeff_ml_uv: Array1<f64>,
319 pub(crate) coeff_ll_uv: Array1<f64>,
320 pub(crate) a_u: Array1<f64>,
321 pub(crate) a_v: Array1<f64>,
322 pub(crate) a_uv: Array1<f64>,
323 pub(crate) c_u: Array1<f64>,
324 pub(crate) c_v: Array1<f64>,
325 pub(crate) c_uv: Array1<f64>,
326 pub(crate) l_u: Array1<f64>,
327 pub(crate) l_v: Array1<f64>,
328 pub(crate) l_uv: Array1<f64>,
329 pub(crate) dw_u: Array1<f64>,
330 pub(crate) dw_v: Array1<f64>,
331 pub(crate) dw_uv: Array1<f64>,
332}
333
334pub(crate) struct GlsWiggleDirPieces<'a> {
338 pub(crate) zeta_u: &'a Array1<f64>,
339 pub(crate) zeta_v: &'a Array1<f64>,
340 pub(crate) q_u: &'a Array1<f64>,
341 pub(crate) q_v: &'a Array1<f64>,
342 pub(crate) q_uv: &'a Array1<f64>,
343 pub(crate) s1_u: &'a Array1<f64>,
344 pub(crate) s1_v: &'a Array1<f64>,
345 pub(crate) s1_uv: &'a Array1<f64>,
346 pub(crate) g2_u: &'a Array1<f64>,
347 pub(crate) g2_v: &'a Array1<f64>,
348 pub(crate) g2_uv: &'a Array1<f64>,
349}
350
351pub(crate) fn gls_wiggle_second_directional_coeffs(
354 rows: &GaussianJointRowScalars,
355 geom: &GaussianLocationScaleWiggleGeometry,
356 dir: &GlsWiggleDirPieces<'_>,
357) -> GlsWiggleSecondDirCoeffs {
358 let GlsWiggleDirPieces {
359 zeta_u,
360 zeta_v,
361 q_u,
362 q_v,
363 q_uv,
364 s1_u,
365 s1_v,
366 s1_uv,
367 g2_u,
368 g2_v,
369 g2_uv,
370 } = *dir;
371 let szeta_u = &rows.kappa * zeta_u;
372 let szeta_v = &rows.kappa * zeta_v;
373 let zeta_u_zeta_v = zeta_u * zeta_v;
374 let dw_u = -2.0 * &rows.w * &szeta_u;
375 let dw_v = -2.0 * &rows.w * &szeta_v;
376 let dw_uv =
377 4.0 * &rows.w * &(&szeta_u * &szeta_v) - 2.0 * &rows.w * &rows.kappa_prime * &zeta_u_zeta_v;
378 let dm_u = -(&rows.w * q_u) - &(2.0 * &rows.m * &szeta_u);
379 let dm_v = -(&rows.w * q_v) - &(2.0 * &rows.m * &szeta_v);
380 let dm_uv = &(2.0 * &rows.w * &(q_u * &szeta_v + q_v * &szeta_u)) - &(&rows.w * q_uv)
381 + &(4.0 * &rows.m * &(&szeta_u * &szeta_v))
382 - 2.0 * &rows.m * &rows.kappa_prime * &zeta_u_zeta_v;
383 let coeff_mm_uv = &(&dw_uv * &geom.dq_dq0.mapv(|v| v * v))
384 + &(2.0 * &dw_u * &geom.dq_dq0 * s1_v)
385 + &(2.0 * &dw_v * &geom.dq_dq0 * s1_u)
386 + &(2.0 * &rows.w * s1_u * s1_v)
387 + &(2.0 * &rows.w * &geom.dq_dq0 * s1_uv)
388 - &(&dm_uv * &geom.d2q_dq02)
389 - &(&dm_u * g2_v)
390 - &(&dm_v * g2_u)
391 - &(&rows.m * g2_uv);
392 let dn_u = &(-(2.0 * &rows.m * q_u)) - &(2.0 * &rows.n * &szeta_u);
395 let dn_v = &(-(2.0 * &rows.m * q_v)) - &(2.0 * &rows.n * &szeta_v);
396 let dn_uv = &(&(&(2.0 * &rows.w * &(q_u * q_v)) - &(2.0 * &rows.m * q_uv))
397 + &(4.0 * &rows.m * &(&(q_u * &szeta_v) + &(q_v * &szeta_u))))
398 + &(&(4.0 * &rows.n * &(&szeta_u * &szeta_v))
399 - &(2.0 * &rows.n * &rows.kappa_prime * &zeta_u_zeta_v));
400 let ktp = &(&rows.kappa_dprime * &(1.0 - 2.0 * &rows.kappa))
402 - &(2.0 * &rows.kappa_prime * &rows.kappa_prime);
403 let a_coef = 2.0 * &rows.kappa * &rows.kappa - &rows.kappa_prime;
404 let amn = &rows.obs_weight - &rows.n;
405 let d = &geom.dq_dq0;
407 let ml_term_kpp = &rows.kappa_dprime * &zeta_u_zeta_v * &rows.m * d;
408 let ml_term_kp_d = &rows.kappa_prime * d * &(&(zeta_v * &dm_u) + &(zeta_u * &dm_v));
409 let ml_term_kp_m = &rows.kappa_prime * &rows.m * &(&(zeta_v * s1_u) + &(zeta_u * s1_v));
410 let ml_term_k_d = &rows.kappa * d * &dm_uv;
411 let ml_term_k_s = &rows.kappa * &(&(&dm_v * s1_u) + &(&dm_u * s1_v));
412 let ml_term_k_m = &rows.kappa * &rows.m * s1_uv;
413 let coeff_ml_uv = 2.0
414 * &(&(&(&(&(&ml_term_kpp + &ml_term_kp_d) + &ml_term_kp_m) + &ml_term_k_d) + &ml_term_k_s)
415 + &ml_term_k_m);
416 let four_kkp_minus_kdp = 4.0 * &rows.kappa * &rows.kappa_prime - &rows.kappa_dprime;
419 let coeff_ll_uv = &(&(&(&ktp * &amn * &zeta_u_zeta_v)
420 + &(4.0
421 * &(&(&rows.kappa_prime * &rows.kappa_prime) + &(&rows.kappa * &rows.kappa_dprime))
422 * &rows.n
423 * &zeta_u_zeta_v))
424 + &(&four_kkp_minus_kdp * &(&(zeta_v * &dn_u) + &(zeta_u * &dn_v))))
425 + &(&a_coef * &dn_uv);
426
427 let a_u = &dw_u * &geom.dq_dq0 + &rows.w * s1_u;
428 let a_v = &dw_v * &geom.dq_dq0 + &rows.w * s1_v;
429 let a_uv = &dw_uv * &geom.dq_dq0 + &dw_u * s1_v + &dw_v * s1_u + &rows.w * s1_uv;
430 let c_u = -&dm_u;
431 let c_v = -&dm_v;
432 let c_uv = -&dm_uv;
433 let l_u = &(2.0 * &rows.kappa_prime * zeta_u * &rows.m) + &(2.0 * &rows.kappa * &dm_u);
435 let l_v = &(2.0 * &rows.kappa_prime * zeta_v * &rows.m) + &(2.0 * &rows.kappa * &dm_v);
436 let l_uv = &(&(2.0 * &rows.kappa_dprime * &zeta_u_zeta_v * &rows.m)
437 + &(2.0 * &rows.kappa_prime * &(&(zeta_v * &dm_u) + &(zeta_u * &dm_v))))
438 + &(2.0 * &rows.kappa * &dm_uv);
439
440 GlsWiggleSecondDirCoeffs {
441 coeff_mm_uv,
442 coeff_ml_uv,
443 coeff_ll_uv,
444 a_u,
445 a_v,
446 a_uv,
447 c_u,
448 c_v,
449 c_uv,
450 l_u,
451 l_v,
452 l_uv,
453 dw_u,
454 dw_v,
455 dw_uv,
456 }
457}
458
459impl GaussianLocationScaleWiggleFamily {
460 pub(crate) fn exact_newton_joint_hessian_for_specs(
461 &self,
462 block_states: &[ParameterBlockState],
463 specs: Option<&[ParameterBlockSpec]>,
464 ) -> Result<Option<Array2<f64>>, String> {
465 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(specs)? else {
466 return Ok(None);
467 };
468 self.exact_newton_joint_hessian_from_designs(block_states, &xmu, &x_ls)
469 }
470
471 pub(crate) fn exact_newton_joint_hessian_directional_derivative_for_specs(
472 &self,
473 block_states: &[ParameterBlockState],
474 specs: Option<&[ParameterBlockSpec]>,
475 d_beta_flat: &Array1<f64>,
476 ) -> Result<Option<Array2<f64>>, String> {
477 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(specs)? else {
478 return Ok(None);
479 };
480 self.exact_newton_joint_hessian_directional_derivative_from_designs(
481 block_states,
482 &xmu,
483 &x_ls,
484 d_beta_flat,
485 )
486 }
487
488 pub(crate) fn exact_newton_joint_hessian_second_directional_derivative_for_specs(
489 &self,
490 block_states: &[ParameterBlockState],
491 specs: Option<&[ParameterBlockSpec]>,
492 d_beta_u_flat: &Array1<f64>,
493 d_beta_v_flat: &Array1<f64>,
494 ) -> Result<Option<Array2<f64>>, String> {
495 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(specs)? else {
496 return Ok(None);
497 };
498 self.exact_newton_joint_hessiansecond_directional_derivative_from_designs(
499 block_states,
500 &xmu,
501 &x_ls,
502 d_beta_u_flat,
503 d_beta_v_flat,
504 )
505 }
506
507 pub(crate) fn exact_newton_joint_psi_direction(
508 &self,
509 block_states: &[ParameterBlockState],
510 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
511 psi_index: usize,
512 xmu: &Array2<f64>,
513 x_ls: &Array2<f64>,
514 policy: &gam_runtime::resource::ResourcePolicy,
515 ) -> Result<Option<LocationScaleJointPsiDirection>, String> {
516 let Some(parts) = locscale_joint_psi_direction_parts(
517 block_states,
518 derivative_blocks,
519 psi_index,
520 self.y.len(),
521 xmu.ncols(),
522 x_ls.ncols(),
523 Self::BLOCK_MU,
524 Self::BLOCK_LOG_SIGMA,
525 3,
526 "GaussianLocationScaleWiggleFamily",
527 "mu",
528 policy,
529 )?
530 else {
531 return Ok(None);
532 };
533 Ok(Some(LocationScaleJointPsiDirection {
534 block_idx: parts.block_idx,
535 local_idx: parts.local_idx,
536 z_primary_psi: parts.primary_z,
537 z_ls_psi: parts.log_sigma_z,
538 x_primary_psi: parts.primary_psi,
539 x_ls_psi: parts.log_sigma_psi,
540 }))
541 }
542
543 pub(crate) fn exact_newton_joint_psisecond_design_drifts(
544 &self,
545 block_states: &[ParameterBlockState],
546 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
547 psi_a: &LocationScaleJointPsiDirection,
548 psi_b: &LocationScaleJointPsiDirection,
549 xmu: &Array2<f64>,
550 x_ls: &Array2<f64>,
551 ) -> Result<LocationScaleJointPsiSecondDrifts, String> {
552 locscale_joint_psisecond_design_drifts(
553 block_states,
554 derivative_blocks,
555 psi_a,
556 psi_b,
557 LocScalePsiDriftConfig {
558 n: self.y.len(),
559 p_primary: xmu.ncols(),
560 p_log_sigma: x_ls.ncols(),
561 primary_block_idx: Self::BLOCK_MU,
562 log_sigma_block_idx: Self::BLOCK_LOG_SIGMA,
563 family_name: "GaussianLocationScaleWiggleFamily",
564 primary_label: "mu",
565 policy: &self.policy,
566 },
567 )
568 }
569
570 pub(crate) fn wiggle_hessian_row_pieces(
574 &self,
575 block_states: &[ParameterBlockState],
576 ) -> Result<GaussianLocationScaleWiggleHessianRowPieces, String> {
577 validate_block_count::<GamlssError>(
578 "GaussianLocationScaleWiggleFamily",
579 3,
580 block_states.len(),
581 )?;
582 let q0 = &block_states[Self::BLOCK_MU].eta;
583 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
584 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
585 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
586 let n = self.y.len();
587 if q0.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
588 return Err(GamlssError::DimensionMismatch {
589 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
590 }
591 .into());
592 }
593 let q = q0 + etaw;
594 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
595 if geom.basis.ncols() != betaw.len() {
596 return Err(GamlssError::DimensionMismatch { reason: format!(
597 "GaussianLocationScaleWiggleFamily wiggle basis/beta mismatch: basis has {} columns but beta has {} entries",
598 geom.basis.ncols(),
599 betaw.len()
600 ) }.into());
601 }
602 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
603 let coeff_mm = &rows.w * &geom.dq_dq0.mapv(|v| v * v) - &rows.m * &geom.d2q_dq02;
604 let (_, ml_base, ll) = gaussian_locscale_observed_joint_row_coeffs(&rows);
619 let coeff_ml = &ml_base * &geom.dq_dq0;
620 let coeff_ll = ll;
621 let coeff_mw_b = &rows.w * &geom.dq_dq0;
622 let coeff_mw_d = -&rows.m;
623 let coeff_lw_b = ml_base;
626 let coeff_ww = rows.w.clone();
627 Ok(GaussianLocationScaleWiggleHessianRowPieces {
628 coeff_mm,
629 coeff_ml,
630 coeff_ll,
631 coeff_mw_b,
632 coeff_mw_d,
633 coeff_lw_b,
634 coeff_ww,
635 basis: geom.basis,
636 basis_d1: geom.basis_d1,
637 })
638 }
639
640 pub(crate) fn exact_newton_joint_hessian_from_designs(
641 &self,
642 block_states: &[ParameterBlockState],
643 xmu: &Array2<f64>,
644 x_ls: &Array2<f64>,
645 ) -> Result<Option<Array2<f64>>, String> {
646 let pieces = self.wiggle_hessian_row_pieces(block_states)?;
647 Ok(Some(pieces.assemble_dense(xmu, x_ls)?))
648 }
649
650 pub(crate) fn exact_newton_joint_hessian_directional_derivative_from_designs(
651 &self,
652 block_states: &[ParameterBlockState],
653 xmu: &Array2<f64>,
654 x_ls: &Array2<f64>,
655 d_beta_flat: &Array1<f64>,
656 ) -> Result<Option<Array2<f64>>, String> {
657 validate_block_count::<GamlssError>(
658 "GaussianLocationScaleWiggleFamily",
659 3,
660 block_states.len(),
661 )?;
662 let pmu = xmu.ncols();
663 let p_ls = x_ls.ncols();
664 let q0 = &block_states[Self::BLOCK_MU].eta;
665 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
666 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
667 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
668 let n = self.y.len();
669 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
670 let (umu, u_ls, uw) = layout.split_three(
671 d_beta_flat,
672 "GaussianLocationScaleWiggleFamily exact joint directional Hessian",
673 )?;
674 if q0.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
675 return Err(GamlssError::DimensionMismatch {
676 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
677 }
678 .into());
679 }
680 let q = q0 + etaw;
681 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
682 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
683 let xi = fast_av(xmu, &umu);
684 let zeta = fast_av(x_ls, &u_ls);
685 let szeta = &rows.kappa * ζ
687 let phi = fast_av(&geom.basis, &uw);
688 let mut q_u = &geom.dq_dq0 * ξ
689 q_u += φ
690 let mut s1_u = &geom.d2q_dq02 * ξ
691 s1_u += &fast_av(&geom.basis_d1, &uw);
692 let mut g2_u = &geom.d3q_dq03 * ξ
693 g2_u += &fast_av(&geom.basis_d2, &uw);
694 let basis_u = scale_matrix_rows(&geom.basis_d1, &xi)?;
695 let basis1_u = scale_matrix_rows(&geom.basis_d2, &xi)?;
696 let dw_u = -2.0 * &rows.w * &szeta;
697 let dm_u = -(&rows.w * &q_u) - &(2.0 * &rows.m * &szeta);
698
699 let coeff_mm_u = &(&dw_u * &geom.dq_dq0.mapv(|v| v * v))
700 + &(2.0 * &rows.w * &geom.dq_dq0 * &s1_u)
701 - &(&dm_u * &geom.d2q_dq02)
702 - &(&rows.m * &g2_u);
703 let a_coef = 2.0 * &rows.kappa * &rows.kappa - &rows.kappa_prime;
710 let dn_u = -(2.0 * &rows.m * &q_u) - &(2.0 * &rows.n * &szeta);
711 let coeff_ml_u = &(2.0 * &rows.kappa_prime * &zeta * &rows.m * &geom.dq_dq0)
712 + &(2.0 * &rows.kappa * &dm_u * &geom.dq_dq0)
713 + &(2.0 * &rows.kappa * &rows.m * &s1_u);
714 let coeff_ll_u = &(&rows.kappa_dprime * &zeta * &(&rows.obs_weight - &rows.n))
715 + &(4.0 * &rows.kappa * &rows.kappa_prime * &zeta * &rows.n)
716 + &(&a_coef * &dn_u);
717 let a_u = &dw_u * &geom.dq_dq0 + &rows.w * &s1_u;
718 let c_u = -&dm_u;
719 let l_u = &(2.0 * &rows.kappa_prime * &zeta * &rows.m) + &(2.0 * &rows.kappa * &dm_u);
723 let coeff_lw_b = 2.0 * &rows.kappa * &rows.m;
724
725 let h_mm = xt_diag_x_dense(xmu, &coeff_mm_u)?;
726 let h_ml = xt_diag_y_dense(xmu, &coeff_ml_u, x_ls)?;
727 let h_ll = xt_diag_x_dense(x_ls, &coeff_ll_u)?;
728 let h_mw = xt_diag_y_dense(xmu, &a_u, &geom.basis)?
729 + &xt_diag_y_dense(xmu, &(&rows.w * &geom.dq_dq0), &basis_u)?
730 + &xt_diag_y_dense(xmu, &c_u, &geom.basis_d1)?
731 + &xt_diag_y_dense(xmu, &(-&rows.m), &basis1_u)?;
732 let h_lw = xt_diag_y_dense(x_ls, &l_u, &geom.basis)?
733 + &xt_diag_y_dense(x_ls, &coeff_lw_b, &basis_u)?;
734 let a_ww = xt_diag_y_dense(&basis_u, &rows.w, &geom.basis)?;
735 let h_ww = &a_ww + &a_ww.t() + &xt_diag_x_dense(&geom.basis, &dw_u)?;
736 Ok(Some(gaussian_pack_wiggle_joint_symmetrichessian(
737 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
738 )))
739 }
740
741 pub(crate) fn gls_wiggle_directional_operator(
745 &self,
746 block_states: &[ParameterBlockState],
747 xmu_arc: Arc<Array2<f64>>,
748 x_ls_arc: Arc<Array2<f64>>,
749 d_beta_flat: &Array1<f64>,
750 ) -> Result<Option<Arc<dyn gam_problem::HyperOperator>>, String> {
751 validate_block_count::<GamlssError>(
752 "GaussianLocationScaleWiggleFamily",
753 3,
754 block_states.len(),
755 )?;
756 let pmu = xmu_arc.ncols();
757 let p_ls = x_ls_arc.ncols();
758 let q0_eta = &block_states[Self::BLOCK_MU].eta;
759 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
760 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
761 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
762 let n = self.y.len();
763 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
764 let (umu, u_ls, uw) =
765 layout.split_three(d_beta_flat, "GLS Wiggle joint dH operator d_beta")?;
766 if q0_eta.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
767 return Err(GamlssError::DimensionMismatch {
768 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
769 }
770 .into());
771 }
772 let q = q0_eta + etaw;
773 let geom = self.wiggle_geometry(q0_eta.view(), betaw.view())?;
774 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
775 let xi = fast_av(xmu_arc.as_ref(), &umu);
776 let zeta = fast_av(x_ls_arc.as_ref(), &u_ls);
777 let szeta = &rows.kappa * ζ
778 let phi = fast_av(&geom.basis, &uw);
779 let mut q_u = &geom.dq_dq0 * ξ
780 q_u += φ
781 let mut s1_u = &geom.d2q_dq02 * ξ
782 s1_u += &fast_av(&geom.basis_d1, &uw);
783 let mut g2_u = &geom.d3q_dq03 * ξ
784 g2_u += &fast_av(&geom.basis_d2, &uw);
785 let dw_u = -2.0 * &rows.w * &szeta;
786 let dm_u = -(&rows.w * &q_u) - &(2.0 * &rows.m * &szeta);
787
788 let coeff_mm_u = &(&dw_u * &geom.dq_dq0.mapv(|v| v * v))
789 + &(2.0 * &rows.w * &geom.dq_dq0 * &s1_u)
790 - &(&dm_u * &geom.d2q_dq02)
791 - &(&rows.m * &g2_u);
792 let coeff_ml_u = &(2.0 * &rows.kappa_prime * &zeta * &rows.m * &geom.dq_dq0)
795 + &(2.0 * &rows.kappa * &dm_u * &geom.dq_dq0)
796 + &(2.0 * &rows.kappa * &rows.m * &s1_u);
797 let a_coef = 2.0 * &rows.kappa * &rows.kappa - &rows.kappa_prime;
800 let dn_u = -(2.0 * &rows.m * &q_u) - &(2.0 * &rows.n * &szeta);
801 let coeff_ll_u = &(&rows.kappa_dprime * &zeta * &(&rows.obs_weight - &rows.n))
802 + &(4.0 * &rows.kappa * &rows.kappa_prime * &zeta * &rows.n)
803 + &(&a_coef * &dn_u);
804 let a_u = &dw_u * &geom.dq_dq0 + &rows.w * &s1_u;
805 let c_u = -&dm_u;
806 let l_u = &(2.0 * &rows.kappa_prime * &zeta * &rows.m) + &(2.0 * &rows.kappa * &dm_u);
809
810 let coeff_m_b1 = &(&rows.w * &geom.dq_dq0 * &xi) + &c_u;
815 let coeff_m_b2 = -(&rows.m * &xi);
817 let coeff_ls_b1 = 2.0 * &rows.kappa * &rows.m * ξ
820 let coeff_b_b1 = &rows.w * ξ
824
825 let basis: Arc<Array2<f64>> = Arc::new(geom.basis.clone());
826 let basis_d1: Arc<Array2<f64>> = Arc::new(geom.basis_d1.clone());
827 let basis_d2: Arc<Array2<f64>> = Arc::new(geom.basis_d2.clone());
828 let pw = basis.ncols();
829
830 Ok(Some(Arc::new(RowCoeffOperator::from_directions(
831 vec![pmu, p_ls, pw],
832 vec![
833 (0, xmu_arc),
834 (1, x_ls_arc),
835 (2, basis),
836 (2, basis_d1),
837 (2, basis_d2),
838 ],
839 vec![
840 (0, 0, coeff_mm_u),
842 (0, 1, coeff_ml_u),
844 (1, 1, coeff_ll_u),
846 (0, 2, a_u),
848 (0, 3, coeff_m_b1),
850 (0, 4, coeff_m_b2),
852 (1, 2, l_u),
854 (1, 3, coeff_ls_b1),
856 (2, 2, dw_u),
858 (2, 3, coeff_b_b1),
860 ],
861 n,
862 ))))
863 }
864
865 pub(crate) fn gls_wiggle_second_directional_operator(
872 &self,
873 block_states: &[ParameterBlockState],
874 xmu_arc: Arc<Array2<f64>>,
875 x_ls_arc: Arc<Array2<f64>>,
876 d_beta_u: &Array1<f64>,
877 d_beta_v: &Array1<f64>,
878 ) -> Result<Option<Arc<dyn gam_problem::HyperOperator>>, String> {
879 validate_block_count::<GamlssError>(
880 "GaussianLocationScaleWiggleFamily",
881 3,
882 block_states.len(),
883 )?;
884 let pmu = xmu_arc.ncols();
885 let p_ls = x_ls_arc.ncols();
886 let q0_eta = &block_states[Self::BLOCK_MU].eta;
887 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
888 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
889 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
890 let n = self.y.len();
891 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
892 let (umu, u_ls, uw) = layout.split_three(d_beta_u, "GLS Wiggle d2H operator (u)")?;
893 let (vmu, v_ls, vw) = layout.split_three(d_beta_v, "GLS Wiggle d2H operator (v)")?;
894 if q0_eta.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
895 return Err(GamlssError::DimensionMismatch {
896 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
897 }
898 .into());
899 }
900 let q = q0_eta + etaw;
901 let geom = self.wiggle_geometry(q0_eta.view(), betaw.view())?;
902 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
903
904 let xi_u = fast_av(xmu_arc.as_ref(), &umu);
905 let xi_v = fast_av(xmu_arc.as_ref(), &vmu);
906 let zeta_u = fast_av(x_ls_arc.as_ref(), &u_ls);
907 let zeta_v = fast_av(x_ls_arc.as_ref(), &v_ls);
908 let phi_u = fast_av(&geom.basis, &uw);
909 let phi_v = fast_av(&geom.basis, &vw);
910 let b1u = fast_av(&geom.basis_d1, &uw);
911 let b1v = fast_av(&geom.basis_d1, &vw);
912 let b2u = fast_av(&geom.basis_d2, &uw);
913 let b2v = fast_av(&geom.basis_d2, &vw);
914 let b3u = fast_av(&geom.basis_d3, &uw);
915 let b3v = fast_av(&geom.basis_d3, &vw);
916
917 let mut q_u = &geom.dq_dq0 * &xi_u;
918 q_u += &phi_u;
919 let mut q_v = &geom.dq_dq0 * &xi_v;
920 q_v += &phi_v;
921 let mut s1_u = &geom.d2q_dq02 * &xi_u;
922 s1_u += &b1u;
923 let mut s1_v = &geom.d2q_dq02 * &xi_v;
924 s1_v += &b1v;
925 let mut g2_u = &geom.d3q_dq03 * &xi_u;
926 g2_u += &b2u;
927 let mut g2_v = &geom.d3q_dq03 * &xi_v;
928 g2_v += &b2v;
929 let q_uv = &(&geom.d2q_dq02 * &(&xi_u * &xi_v)) + &(&b1u * &xi_v) + &(&b1v * &xi_u);
930 let s1_uv = &(&geom.d3q_dq03 * &(&xi_u * &xi_v)) + &(&b2u * &xi_v) + &(&b2v * &xi_u);
931 let g2_uv = &(&geom.d4q_dq04 * &(&xi_u * &xi_v)) + &(&b3u * &xi_v) + &(&b3v * &xi_u);
932
933 let GlsWiggleSecondDirCoeffs {
934 coeff_mm_uv,
935 coeff_ml_uv,
936 coeff_ll_uv,
937 a_u,
938 a_v,
939 a_uv,
940 c_u,
941 c_v,
942 c_uv,
943 l_u,
944 l_v,
945 l_uv,
946 dw_u,
947 dw_v,
948 dw_uv,
949 } = gls_wiggle_second_directional_coeffs(
950 &rows,
951 &geom,
952 &GlsWiggleDirPieces {
953 zeta_u: &zeta_u,
954 zeta_v: &zeta_v,
955 q_u: &q_u,
956 q_v: &q_v,
957 q_uv: &q_uv,
958 s1_u: &s1_u,
959 s1_v: &s1_v,
960 s1_uv: &s1_uv,
961 g2_u: &g2_u,
962 g2_v: &g2_v,
963 g2_uv: &g2_uv,
964 },
965 );
966
967 let xi_u_xi_v = &xi_u * &xi_v;
969 let coeff_m_b1 = &(&a_u * &xi_v) + &(&a_v * &xi_u) + &c_uv;
970 let coeff_m_b2 = &(&rows.w * &geom.dq_dq0 * &xi_u_xi_v) + &(&c_u * &xi_v) + &(&c_v * &xi_u);
971 let coeff_m_b3 = -(&rows.m * &xi_u_xi_v);
972 let coeff_ls_b1 = &(&l_u * &xi_v) + &(&l_v * &xi_u);
976 let coeff_ls_b2 = 2.0 * &rows.kappa * &rows.m * &xi_u_xi_v;
977 let coeff_b_b1 = &(&dw_u * &xi_v) + &(&dw_v * &xi_u);
982 let coeff_b_b2 = &rows.w * &xi_u_xi_v;
983 let coeff_b1_b1 = 2.0 * &(&rows.w * &xi_u_xi_v);
984
985 let basis: Arc<Array2<f64>> = Arc::new(geom.basis.clone());
986 let basis_d1: Arc<Array2<f64>> = Arc::new(geom.basis_d1.clone());
987 let basis_d2: Arc<Array2<f64>> = Arc::new(geom.basis_d2.clone());
988 let basis_d3: Arc<Array2<f64>> = Arc::new(geom.basis_d3.clone());
989 let pw = basis.ncols();
990
991 Ok(Some(Arc::new(RowCoeffOperator::from_directions(
992 vec![pmu, p_ls, pw],
993 vec![
994 (0, xmu_arc),
995 (1, x_ls_arc),
996 (2, basis),
997 (2, basis_d1),
998 (2, basis_d2),
999 (2, basis_d3),
1000 ],
1001 vec![
1002 (0, 0, coeff_mm_uv),
1004 (0, 1, coeff_ml_uv),
1006 (1, 1, coeff_ll_uv),
1008 (0, 2, a_uv),
1010 (0, 3, coeff_m_b1),
1014 (0, 4, coeff_m_b2),
1019 (0, 5, coeff_m_b3),
1022 (1, 2, l_uv),
1024 (1, 3, coeff_ls_b1),
1028 (1, 4, coeff_ls_b2),
1030 (2, 2, dw_uv),
1032 (2, 3, coeff_b_b1),
1035 (2, 4, coeff_b_b2),
1037 (3, 3, coeff_b1_b1),
1040 ],
1041 n,
1042 ))))
1043 }
1044
1045 pub(crate) fn exact_newton_joint_hessiansecond_directional_derivative_from_designs(
1046 &self,
1047 block_states: &[ParameterBlockState],
1048 xmu: &Array2<f64>,
1049 x_ls: &Array2<f64>,
1050 d_beta_u_flat: &Array1<f64>,
1051 d_beta_v_flat: &Array1<f64>,
1052 ) -> Result<Option<Array2<f64>>, String> {
1053 validate_block_count::<GamlssError>(
1054 "GaussianLocationScaleWiggleFamily",
1055 3,
1056 block_states.len(),
1057 )?;
1058 let pmu = xmu.ncols();
1059 let p_ls = x_ls.ncols();
1060 let q0 = &block_states[Self::BLOCK_MU].eta;
1061 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
1062 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
1063 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
1064 let n = self.y.len();
1065 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
1066 let (umu, u_ls, uw) = layout.split_three(
1067 d_beta_u_flat,
1068 "GaussianLocationScaleWiggleFamily exact joint second directional Hessian (u)",
1069 )?;
1070 let (vmu, v_ls, vw) = layout.split_three(
1071 d_beta_v_flat,
1072 "GaussianLocationScaleWiggleFamily exact joint second directional Hessian (v)",
1073 )?;
1074 if q0.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
1075 return Err(GamlssError::DimensionMismatch {
1076 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
1077 }
1078 .into());
1079 }
1080 let q = q0 + etaw;
1081 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
1082 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
1083
1084 let xi_u = fast_av(xmu, &umu);
1085 let xi_v = fast_av(xmu, &vmu);
1086 let zeta_u = fast_av(x_ls, &u_ls);
1087 let zeta_v = fast_av(x_ls, &v_ls);
1088 let phi_u = fast_av(&geom.basis, &uw);
1089 let phi_v = fast_av(&geom.basis, &vw);
1090 let b1u = fast_av(&geom.basis_d1, &uw);
1091 let b1v = fast_av(&geom.basis_d1, &vw);
1092 let b2u = fast_av(&geom.basis_d2, &uw);
1093 let b2v = fast_av(&geom.basis_d2, &vw);
1094 let b3u = fast_av(&geom.basis_d3, &uw);
1095 let b3v = fast_av(&geom.basis_d3, &vw);
1096
1097 let mut q_u = &geom.dq_dq0 * &xi_u;
1098 q_u += &phi_u;
1099 let mut q_v = &geom.dq_dq0 * &xi_v;
1100 q_v += &phi_v;
1101 let mut s1_u = &geom.d2q_dq02 * &xi_u;
1102 s1_u += &b1u;
1103 let mut s1_v = &geom.d2q_dq02 * &xi_v;
1104 s1_v += &b1v;
1105 let mut g2_u = &geom.d3q_dq03 * &xi_u;
1106 g2_u += &b2u;
1107 let mut g2_v = &geom.d3q_dq03 * &xi_v;
1108 g2_v += &b2v;
1109 let q_uv = &(&geom.d2q_dq02 * &(&xi_u * &xi_v)) + &(&b1u * &xi_v) + &(&b1v * &xi_u);
1110 let s1_uv = &(&geom.d3q_dq03 * &(&xi_u * &xi_v)) + &(&b2u * &xi_v) + &(&b2v * &xi_u);
1111 let g2_uv = &(&geom.d4q_dq04 * &(&xi_u * &xi_v)) + &(&b3u * &xi_v) + &(&b3v * &xi_u);
1112
1113 let basis_u = scale_matrix_rows(&geom.basis_d1, &xi_u)?;
1114 let basis_v = scale_matrix_rows(&geom.basis_d1, &xi_v)?;
1115 let basis_uv = scale_matrix_rows(&geom.basis_d2, &(&xi_u * &xi_v))?;
1116 let basis1_u = scale_matrix_rows(&geom.basis_d2, &xi_u)?;
1117 let basis1_v = scale_matrix_rows(&geom.basis_d2, &xi_v)?;
1118 let basis1_uv = scale_matrix_rows(&geom.basis_d3, &(&xi_u * &xi_v))?;
1119
1120 let GlsWiggleSecondDirCoeffs {
1124 coeff_mm_uv,
1125 coeff_ml_uv,
1126 coeff_ll_uv,
1127 a_u,
1128 a_v,
1129 a_uv,
1130 c_u,
1131 c_v,
1132 c_uv,
1133 l_u,
1134 l_v,
1135 l_uv,
1136 dw_u,
1137 dw_v,
1138 dw_uv,
1139 } = gls_wiggle_second_directional_coeffs(
1140 &rows,
1141 &geom,
1142 &GlsWiggleDirPieces {
1143 zeta_u: &zeta_u,
1144 zeta_v: &zeta_v,
1145 q_u: &q_u,
1146 q_v: &q_v,
1147 q_uv: &q_uv,
1148 s1_u: &s1_u,
1149 s1_v: &s1_v,
1150 s1_uv: &s1_uv,
1151 g2_u: &g2_u,
1152 g2_v: &g2_v,
1153 g2_uv: &g2_uv,
1154 },
1155 );
1156
1157 let h_mm = xt_diag_x_dense(xmu, &coeff_mm_uv)?;
1158 let h_ml = xt_diag_y_dense(xmu, &coeff_ml_uv, x_ls)?;
1159 let h_ll = xt_diag_x_dense(x_ls, &coeff_ll_uv)?;
1160 let h_mw = xt_diag_y_dense(xmu, &a_uv, &geom.basis)?
1161 + &xt_diag_y_dense(xmu, &a_u, &basis_v)?
1162 + &xt_diag_y_dense(xmu, &a_v, &basis_u)?
1163 + &xt_diag_y_dense(xmu, &(&rows.w * &geom.dq_dq0), &basis_uv)?
1164 + &xt_diag_y_dense(xmu, &c_uv, &geom.basis_d1)?
1165 + &xt_diag_y_dense(xmu, &c_u, &basis1_v)?
1166 + &xt_diag_y_dense(xmu, &c_v, &basis1_u)?
1167 + &xt_diag_y_dense(xmu, &(-&rows.m), &basis1_uv)?;
1168 let coeff_lw_b = 2.0 * &rows.kappa * &rows.m;
1172 let h_lw = xt_diag_y_dense(x_ls, &l_uv, &geom.basis)?
1173 + &xt_diag_y_dense(x_ls, &l_u, &basis_v)?
1174 + &xt_diag_y_dense(x_ls, &l_v, &basis_u)?
1175 + &xt_diag_y_dense(x_ls, &coeff_lw_b, &basis_uv)?;
1176 let a_ab = xt_diag_y_dense(&basis_uv, &rows.w, &geom.basis)?;
1177 let a_ij = xt_diag_y_dense(&basis_u, &rows.w, &basis_v)?;
1178 let a_iwj = xt_diag_y_dense(&basis_u, &dw_v, &geom.basis)?;
1179 let a_jwi = xt_diag_y_dense(&basis_v, &dw_u, &geom.basis)?;
1180 let h_ww = &a_ab
1181 + &a_ab.t()
1182 + &a_ij
1183 + a_ij.t()
1184 + &a_iwj
1185 + a_iwj.t()
1186 + &a_jwi
1187 + a_jwi.t()
1188 + &xt_diag_x_dense(&geom.basis, &dw_uv)?;
1189 Ok(Some(gaussian_pack_wiggle_joint_symmetrichessian(
1190 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
1191 )))
1192 }
1193
1194 pub(crate) fn exact_newton_joint_psi_terms_from_designs(
1195 &self,
1196 block_states: &[ParameterBlockState],
1197 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1198 psi_index: usize,
1199 xmu: &Array2<f64>,
1200 x_ls: &Array2<f64>,
1201 ) -> Result<Option<gam_problem::ExactNewtonJointPsiTerms>, String> {
1202 let Some(dir_a) = self.exact_newton_joint_psi_direction(
1203 block_states,
1204 derivative_blocks,
1205 psi_index,
1206 xmu,
1207 x_ls,
1208 &self.policy,
1209 )?
1210 else {
1211 return Ok(None);
1212 };
1213 let q0 = &block_states[Self::BLOCK_MU].eta;
1214 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
1215 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
1216 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
1217 let q = q0 + etaw;
1218 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
1219 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
1220 let xmu_map = dir_a.x_primary_psi.as_linear_map_ref();
1221 let x_ls_map = dir_a.x_ls_psi.as_linear_map_ref();
1222
1223 let q_a = &geom.dq_dq0 * &dir_a.z_primary_psi;
1224 let s1_a = &geom.d2q_dq02 * &dir_a.z_primary_psi;
1225 let g2_a = &geom.d3q_dq03 * &dir_a.z_primary_psi;
1226 let basis_a = scale_matrix_rows(&geom.basis_d1, &dir_a.z_primary_psi)?;
1227 let basis1_a = scale_matrix_rows(&geom.basis_d2, &dir_a.z_primary_psi)?;
1228 let e_a = &dir_a.z_ls_psi;
1230 let amn = &rows.obs_weight - &rows.n;
1231 let dw_a = -2.0 * &rows.w * &rows.kappa * e_a;
1232 let dm_a = -(&rows.w * &q_a) - &(2.0 * &rows.m * &rows.kappa * e_a);
1233 let dn_a = -(2.0 * &rows.m * &q_a) - &(2.0 * &rows.n * &rows.kappa * e_a);
1234 let s_mu = -&rows.m * &geom.dq_dq0;
1235 let s_mu_a = -(&dm_a * &geom.dq_dq0) - &(&rows.m * &s1_a);
1236 let s_ls = &rows.kappa * &amn;
1237 let s_ls_a = &rows.kappa_prime * &(e_a * &amn) - &rows.kappa * &dn_a;
1238 let s_w = -&rows.m;
1239 let s_w_a = -&dm_a;
1240
1241 let objective_psi = (-&rows.m * &q_a + &s_ls * e_a).sum();
1242 let score_psi = gaussian_pack_wiggle_joint_score(
1243 &(xmu_map.transpose_mul(s_mu.view()) + fast_atv(xmu, &s_mu_a)),
1244 &(x_ls_map.transpose_mul(s_ls.view()) + fast_atv(x_ls, &s_ls_a)),
1245 &(fast_atv(&basis_a, &s_w) + fast_atv(&geom.basis, &s_w_a)),
1246 );
1247
1248 let coeff_mm = &rows.w * &geom.dq_dq0.mapv(|v| v * v) - &rows.m * &geom.d2q_dq02;
1260 let coeff_mm_a = &(&dw_a * &geom.dq_dq0.mapv(|v| v * v))
1261 + &(2.0 * &rows.w * &geom.dq_dq0 * &s1_a)
1262 - &(&dm_a * &geom.d2q_dq02)
1263 - &(&rows.m * &g2_a);
1264 let a_coef = 2.0 * &rows.kappa * &rows.kappa - &rows.kappa_prime;
1265 let coeff_ml = 2.0 * &rows.kappa * &rows.m * &geom.dq_dq0;
1266 let coeff_ml_a = &(2.0 * &rows.kappa_prime * e_a * &rows.m * &geom.dq_dq0)
1267 + &(&(2.0 * &rows.kappa * &dm_a * &geom.dq_dq0)
1268 + &(2.0 * &rows.kappa * &rows.m * &s1_a));
1269 let coeff_ll = &rows.kappa_prime * &amn + 2.0 * &rows.kappa * &rows.kappa * &rows.n;
1270 let coeff_ll_a = &(&(&rows.kappa_dprime * e_a * &amn)
1271 + &(4.0 * &rows.kappa * &rows.kappa_prime * e_a * &rows.n))
1272 + &(&a_coef * &dn_a);
1273 let a = &rows.w * &geom.dq_dq0;
1274 let a_a = &dw_a * &geom.dq_dq0 + &rows.w * &s1_a;
1275 let c = -&rows.m;
1276 let c_a = -&dm_a;
1277 let l = 2.0 * &rows.kappa * &rows.m;
1278 let l_a = &(2.0 * &rows.kappa_prime * e_a * &rows.m) + &(2.0 * &rows.kappa * &dm_a);
1279 let h_mm_a1 = weighted_crossprod_psi_maps(
1280 xmu_map,
1281 coeff_mm.view(),
1282 CustomFamilyPsiLinearMapRef::Dense(xmu),
1283 )?;
1284 let h_mm = &h_mm_a1 + &h_mm_a1.t() + &xt_diag_x_dense(xmu, &coeff_mm_a)?;
1285 let h_ml = weighted_crossprod_psi_maps(
1286 xmu_map,
1287 coeff_ml.view(),
1288 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1289 )? + &weighted_crossprod_psi_maps(
1290 CustomFamilyPsiLinearMapRef::Dense(xmu),
1291 coeff_ml.view(),
1292 x_ls_map,
1293 )? + &xt_diag_y_dense(xmu, &coeff_ml_a, x_ls)?;
1294 let h_ll_a1 = weighted_crossprod_psi_maps(
1295 x_ls_map,
1296 coeff_ll.view(),
1297 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1298 )?;
1299 let h_ll = &h_ll_a1 + &h_ll_a1.t() + &xt_diag_x_dense(x_ls, &coeff_ll_a)?;
1300 let h_mw = weighted_crossprod_psi_maps(
1301 xmu_map,
1302 a.view(),
1303 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1304 )? + &xt_diag_y_dense(xmu, &a_a, &geom.basis)?
1305 + &xt_diag_y_dense(xmu, &a, &basis_a)?
1306 + &weighted_crossprod_psi_maps(
1307 xmu_map,
1308 c.view(),
1309 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1310 )?
1311 + &xt_diag_y_dense(xmu, &c_a, &geom.basis_d1)?
1312 + &xt_diag_y_dense(xmu, &c, &basis1_a)?;
1313 let h_lw = weighted_crossprod_psi_maps(
1314 x_ls_map,
1315 l.view(),
1316 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1317 )? + &xt_diag_y_dense(x_ls, &l_a, &geom.basis)?
1318 + &xt_diag_y_dense(x_ls, &l, &basis_a)?;
1319 let h_ww_a1 = xt_diag_y_dense(&basis_a, &rows.w, &geom.basis)?;
1320 let h_ww = &h_ww_a1 + &h_ww_a1.t() + &xt_diag_x_dense(&geom.basis, &dw_a)?;
1321
1322 Ok(Some(gam_problem::ExactNewtonJointPsiTerms {
1323 objective_psi,
1324 score_psi,
1325 hessian_psi: gaussian_pack_wiggle_joint_symmetrichessian(
1326 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
1327 ),
1328 hessian_psi_operator: None,
1329 }))
1330 }
1331
1332 pub(crate) fn exact_newton_joint_psisecond_order_terms_from_designs(
1333 &self,
1334 block_states: &[ParameterBlockState],
1335 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1336 psi_i: usize,
1337 psi_j: usize,
1338 xmu: &Array2<f64>,
1339 x_ls: &Array2<f64>,
1340 ) -> Result<Option<gam_problem::ExactNewtonJointPsiSecondOrderTerms>, String> {
1341 let Some(dir_a) = self.exact_newton_joint_psi_direction(
1342 block_states,
1343 derivative_blocks,
1344 psi_i,
1345 xmu,
1346 x_ls,
1347 &self.policy,
1348 )?
1349 else {
1350 return Ok(None);
1351 };
1352 let Some(dir_b) = self.exact_newton_joint_psi_direction(
1353 block_states,
1354 derivative_blocks,
1355 psi_j,
1356 xmu,
1357 x_ls,
1358 &self.policy,
1359 )?
1360 else {
1361 return Ok(None);
1362 };
1363 Ok(Some(
1364 self.exact_newton_joint_psisecond_order_terms_from_parts(
1365 block_states,
1366 derivative_blocks,
1367 &dir_a,
1368 &dir_b,
1369 xmu,
1370 x_ls,
1371 )?,
1372 ))
1373 }
1374
1375 pub(crate) fn exact_newton_joint_psisecond_order_terms_from_parts(
1376 &self,
1377 block_states: &[ParameterBlockState],
1378 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1379 dir_a: &LocationScaleJointPsiDirection,
1380 dir_b: &LocationScaleJointPsiDirection,
1381 xmu: &Array2<f64>,
1382 x_ls: &Array2<f64>,
1383 ) -> Result<gam_problem::ExactNewtonJointPsiSecondOrderTerms, String> {
1384 let second_drifts = self.exact_newton_joint_psisecond_design_drifts(
1385 block_states,
1386 derivative_blocks,
1387 dir_a,
1388 dir_b,
1389 xmu,
1390 x_ls,
1391 )?;
1392 let n = self.y.len();
1393 let xmu_a_map = dir_a.x_primary_psi.as_linear_map_ref();
1394 let x_ls_a_map = dir_a.x_ls_psi.as_linear_map_ref();
1395 let xmu_b_map = dir_b.x_primary_psi.as_linear_map_ref();
1396 let x_ls_b_map = dir_b.x_ls_psi.as_linear_map_ref();
1397 let xmu_ab_map = second_psi_linear_map(
1398 second_drifts.x_primary_ab_action.as_ref(),
1399 second_drifts.x_primary_ab.as_ref(),
1400 n,
1401 xmu.ncols(),
1402 );
1403 let x_ls_ab_map = second_psi_linear_map(
1404 second_drifts.x_ls_ab_action.as_ref(),
1405 second_drifts.x_ls_ab.as_ref(),
1406 n,
1407 x_ls.ncols(),
1408 );
1409 let q0 = &block_states[Self::BLOCK_MU].eta;
1410 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
1411 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
1412 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
1413 let q = q0 + etaw;
1414 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
1415 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
1416
1417 let q_a = &geom.dq_dq0 * &dir_a.z_primary_psi;
1418 let q_b = &geom.dq_dq0 * &dir_b.z_primary_psi;
1419 let q_ab = &(&geom.dq_dq0 * &second_drifts.z_primary_ab)
1420 + &(&geom.d2q_dq02 * &(&dir_a.z_primary_psi * &dir_b.z_primary_psi));
1421 let s1_a = &geom.d2q_dq02 * &dir_a.z_primary_psi;
1422 let s1_b = &geom.d2q_dq02 * &dir_b.z_primary_psi;
1423 let s1_ab = &(&geom.d3q_dq03 * &(&dir_a.z_primary_psi * &dir_b.z_primary_psi))
1424 + &(&geom.d2q_dq02 * &second_drifts.z_primary_ab);
1425 let g2_a = &geom.d3q_dq03 * &dir_a.z_primary_psi;
1426 let g2_b = &geom.d3q_dq03 * &dir_b.z_primary_psi;
1427 let g2_ab = &(&geom.d4q_dq04 * &(&dir_a.z_primary_psi * &dir_b.z_primary_psi))
1428 + &(&geom.d3q_dq03 * &second_drifts.z_primary_ab);
1429 let basis_a = scale_matrix_rows(&geom.basis_d1, &dir_a.z_primary_psi)?;
1430 let basis_b = scale_matrix_rows(&geom.basis_d1, &dir_b.z_primary_psi)?;
1431 let basis_ab = scale_matrix_rows(&geom.basis_d1, &second_drifts.z_primary_ab)?
1432 + &scale_matrix_rows(
1433 &geom.basis_d2,
1434 &(&dir_a.z_primary_psi * &dir_b.z_primary_psi),
1435 )?;
1436 let basis1_a = scale_matrix_rows(&geom.basis_d2, &dir_a.z_primary_psi)?;
1437 let basis1_b = scale_matrix_rows(&geom.basis_d2, &dir_b.z_primary_psi)?;
1438 let basis1_ab = scale_matrix_rows(&geom.basis_d2, &second_drifts.z_primary_ab)?
1439 + &scale_matrix_rows(
1440 &geom.basis_d3,
1441 &(&dir_a.z_primary_psi * &dir_b.z_primary_psi),
1442 )?;
1443
1444 let e_a = &dir_a.z_ls_psi;
1447 let e_b = &dir_b.z_ls_psi;
1448 let e_ab = &second_drifts.z_ls_ab;
1449 let amn = &rows.obs_weight - &rows.n;
1450 let four_k2_minus_2kpi = 4.0 * &rows.kappa * &rows.kappa - 2.0 * &rows.kappa_prime;
1452
1453 let dw_a = -2.0 * &rows.w * &rows.kappa * e_a;
1457 let dw_b = -2.0 * &rows.w * &rows.kappa * e_b;
1458 let dw_ab =
1459 &four_k2_minus_2kpi * &rows.w * &(e_a * e_b) - &(2.0 * &rows.w * &rows.kappa * e_ab);
1460 let dm_a = -(&rows.w * &q_a) - &(2.0 * &rows.m * &rows.kappa * e_a);
1461 let dm_b = -(&rows.w * &q_b) - &(2.0 * &rows.m * &rows.kappa * e_b);
1462 let dm_ab = &(2.0 * &rows.w * &rows.kappa * &(&q_a * e_b + &q_b * e_a))
1463 - &(&rows.w * &q_ab)
1464 + &(&four_k2_minus_2kpi * &rows.m * &(e_a * e_b))
1465 - &(2.0 * &rows.m * &rows.kappa * e_ab);
1466 let dn_a = -(2.0 * &rows.m * &q_a) - &(2.0 * &rows.n * &rows.kappa * e_a);
1467 let dn_b = -(2.0 * &rows.m * &q_b) - &(2.0 * &rows.n * &rows.kappa * e_b);
1468 let dn_ab = &(2.0 * &rows.w * &(&q_a * &q_b))
1469 + &(4.0 * &rows.m * &rows.kappa * &(&q_a * e_b + &q_b * e_a))
1470 - &(2.0 * &rows.m * &q_ab)
1471 + &(&four_k2_minus_2kpi * &rows.n * &(e_a * e_b))
1472 - &(2.0 * &rows.n * &rows.kappa * e_ab);
1473
1474 let s_mu = -&rows.m * &geom.dq_dq0;
1475 let s_mu_a = -(&dm_a * &geom.dq_dq0) - &(&rows.m * &s1_a);
1476 let s_mu_b = -(&dm_b * &geom.dq_dq0) - &(&rows.m * &s1_b);
1477 let s_mu_ab =
1478 -(&dm_ab * &geom.dq_dq0) - &(&dm_a * &s1_b) - &(&dm_b * &s1_a) - &(&rows.m * &s1_ab);
1479 let s_ls = &rows.kappa * &amn;
1481 let s_ls_a = &rows.kappa_prime * &(e_a * &amn) - &rows.kappa * &dn_a;
1482 let s_ls_b = &rows.kappa_prime * &(e_b * &amn) - &rows.kappa * &dn_b;
1483 let s_ls_ab = &rows.kappa_dprime * &(e_a * e_b) * &amn + &rows.kappa_prime * e_ab * &amn
1486 - &rows.kappa_prime * &(e_a * &dn_b + e_b * &dn_a)
1487 - &rows.kappa * &dn_ab;
1488 let s_w = -&rows.m;
1489 let s_w_a = -&dm_a;
1490 let s_w_b = -&dm_b;
1491 let s_w_ab = -&dm_ab;
1492
1493 let objective_psi_psi = (&rows.w * &(&q_a * &q_b)
1494 + &(2.0 * &rows.m * &rows.kappa * &(&q_a * e_b + &q_b * e_a))
1495 + &((2.0 * &rows.kappa * &rows.kappa * &rows.n + &rows.kappa_prime * &amn)
1496 * &(e_a * e_b))
1497 - &(&rows.m * &q_ab)
1498 + &(&rows.kappa * &amn * e_ab))
1499 .sum();
1500
1501 let score_psi_psi = gaussian_pack_wiggle_joint_score(
1502 &(xmu_ab_map.transpose_mul(s_mu.view())
1503 + xmu_a_map.transpose_mul(s_mu_b.view())
1504 + xmu_b_map.transpose_mul(s_mu_a.view())
1505 + fast_atv(xmu, &s_mu_ab)),
1506 &(x_ls_ab_map.transpose_mul(s_ls.view())
1507 + x_ls_a_map.transpose_mul(s_ls_b.view())
1508 + x_ls_b_map.transpose_mul(s_ls_a.view())
1509 + fast_atv(x_ls, &s_ls_ab)),
1510 &(fast_atv(&basis_ab, &s_w)
1511 + fast_atv(&basis_a, &s_w_b)
1512 + fast_atv(&basis_b, &s_w_a)
1513 + fast_atv(&geom.basis, &s_w_ab)),
1514 );
1515
1516 let d = &geom.dq_dq0;
1525 let a_coef = 2.0 * &rows.kappa * &rows.kappa - &rows.kappa_prime;
1526 let ktp = &(&rows.kappa_dprime * &(1.0 - 2.0 * &rows.kappa))
1527 - &(2.0 * &rows.kappa_prime * &rows.kappa_prime);
1528 let four_kkp_minus_kdp = 4.0 * &rows.kappa * &rows.kappa_prime - &rows.kappa_dprime;
1529 let coeff_mm = &rows.w * &geom.dq_dq0.mapv(|v| v * v) - &rows.m * &geom.d2q_dq02;
1530 let coeff_ml = 2.0 * &rows.kappa * &rows.m * d;
1531 let coeff_ll = &rows.kappa_prime * &amn + 2.0 * &rows.kappa * &rows.kappa * &rows.n;
1532 let coeff_mm_a = &(&dw_a * &geom.dq_dq0.mapv(|v| v * v))
1535 + &(2.0 * &rows.w * &geom.dq_dq0 * &s1_a)
1536 - &(&dm_a * &geom.d2q_dq02)
1537 - &(&rows.m * &g2_a);
1538 let coeff_mm_b = &(&dw_b * &geom.dq_dq0.mapv(|v| v * v))
1539 + &(2.0 * &rows.w * &geom.dq_dq0 * &s1_b)
1540 - &(&dm_b * &geom.d2q_dq02)
1541 - &(&rows.m * &g2_b);
1542 let coeff_mm_ab = &(&dw_ab * &geom.dq_dq0.mapv(|v| v * v))
1543 + &(2.0 * &dw_a * &geom.dq_dq0 * &s1_b)
1544 + &(2.0 * &dw_b * &geom.dq_dq0 * &s1_a)
1545 + &(2.0 * &rows.w * &s1_a * &s1_b)
1546 + &(2.0 * &rows.w * &geom.dq_dq0 * &s1_ab)
1547 - &(&dm_ab * &geom.d2q_dq02)
1548 - &(&dm_a * &g2_b)
1549 - &(&dm_b * &g2_a)
1550 - &(&rows.m * &g2_ab);
1551 let coeff_ml_a = &(2.0 * &rows.kappa_prime * e_a * &rows.m * d)
1553 + &(&(2.0 * &rows.kappa * &dm_a * d) + &(2.0 * &rows.kappa * &rows.m * &s1_a));
1554 let coeff_ml_b = &(2.0 * &rows.kappa_prime * e_b * &rows.m * d)
1555 + &(&(2.0 * &rows.kappa * &dm_b * d) + &(2.0 * &rows.kappa * &rows.m * &s1_b));
1556 let coeff_ml_ab = 2.0
1557 * &(&(&(&(&(&(&rows.kappa_dprime * &(e_a * e_b) * &rows.m * d)
1558 + &(&rows.kappa_prime * e_ab * &rows.m * d))
1559 + &(&rows.kappa_prime * d * &(&(e_a * &dm_b) + &(e_b * &dm_a))))
1560 + &(&rows.kappa_prime * &rows.m * &(&(e_a * &s1_b) + &(e_b * &s1_a))))
1561 + &(&rows.kappa * d * &dm_ab))
1562 + &(&rows.kappa * &(&(&dm_a * &s1_b) + &(&dm_b * &s1_a))))
1563 + &(2.0 * &rows.kappa * &rows.m * &s1_ab);
1564 let coeff_ll_a = &(&(&rows.kappa_dprime * e_a * &amn)
1566 + &(4.0 * &rows.kappa * &rows.kappa_prime * e_a * &rows.n))
1567 + &(&a_coef * &dn_a);
1568 let coeff_ll_b = &(&(&rows.kappa_dprime * e_b * &amn)
1569 + &(4.0 * &rows.kappa * &rows.kappa_prime * e_b * &rows.n))
1570 + &(&a_coef * &dn_b);
1571 let coeff_ll_ab = &(&(&(&(&ktp * &amn * &(e_a * e_b))
1574 + &(4.0
1575 * &(&(&rows.kappa_prime * &rows.kappa_prime)
1576 + &(&rows.kappa * &rows.kappa_dprime))
1577 * &rows.n
1578 * &(e_a * e_b)))
1579 + &(&four_kkp_minus_kdp * &(&(e_a * &dn_b) + &(e_b * &dn_a))))
1580 + &(&a_coef * &dn_ab))
1581 + &(&(&(&rows.kappa_dprime * &amn)
1582 + &(4.0 * &rows.kappa * &rows.kappa_prime * &rows.n))
1583 * e_ab);
1584 let a = &rows.w * &geom.dq_dq0;
1585 let a_a = &dw_a * &geom.dq_dq0 + &rows.w * &s1_a;
1586 let a_b = &dw_b * &geom.dq_dq0 + &rows.w * &s1_b;
1587 let a_ab = &dw_ab * &geom.dq_dq0 + &dw_a * &s1_b + &dw_b * &s1_a + &rows.w * &s1_ab;
1588 let c = -&rows.m;
1589 let c_a = -&dm_a;
1590 let c_b = -&dm_b;
1591 let c_ab = -&dm_ab;
1592 let l = 2.0 * &rows.kappa * &rows.m;
1594 let l_a = &(2.0 * &rows.kappa_prime * e_a * &rows.m) + &(2.0 * &rows.kappa * &dm_a);
1595 let l_b = &(2.0 * &rows.kappa_prime * e_b * &rows.m) + &(2.0 * &rows.kappa * &dm_b);
1596 let l_ab = &(&(2.0 * &rows.kappa_dprime * &(e_a * e_b) * &rows.m)
1597 + &(2.0 * &rows.kappa_prime * &(&(e_a * &dm_b) + &(e_b * &dm_a))))
1598 + &(2.0 * &rows.kappa * &dm_ab);
1599
1600 let hmm_ab = weighted_crossprod_psi_maps(
1601 xmu_ab_map,
1602 coeff_mm.view(),
1603 CustomFamilyPsiLinearMapRef::Dense(xmu),
1604 )?;
1605 let hmm_ij = weighted_crossprod_psi_maps(xmu_a_map, coeff_mm.view(), xmu_b_map)?;
1606 let hmm_iwj = weighted_crossprod_psi_maps(
1607 xmu_a_map,
1608 coeff_mm_b.view(),
1609 CustomFamilyPsiLinearMapRef::Dense(xmu),
1610 )?;
1611 let hmm_jwi = weighted_crossprod_psi_maps(
1612 xmu_b_map,
1613 coeff_mm_a.view(),
1614 CustomFamilyPsiLinearMapRef::Dense(xmu),
1615 )?;
1616 let h_mm = &hmm_ab
1617 + &hmm_ab.t()
1618 + &hmm_ij
1619 + hmm_ij.t()
1620 + &hmm_iwj
1621 + hmm_iwj.t()
1622 + &hmm_jwi
1623 + hmm_jwi.t()
1624 + &xt_diag_x_dense(xmu, &coeff_mm_ab)?;
1625 let h_ml = weighted_crossprod_psi_maps(
1626 xmu_ab_map,
1627 coeff_ml.view(),
1628 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1629 )? + &weighted_crossprod_psi_maps(xmu_a_map, coeff_ml.view(), x_ls_b_map)?
1630 + &weighted_crossprod_psi_maps(xmu_b_map, coeff_ml.view(), x_ls_a_map)?
1631 + &weighted_crossprod_psi_maps(
1632 xmu_a_map,
1633 coeff_ml_b.view(),
1634 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1635 )?
1636 + &weighted_crossprod_psi_maps(
1637 xmu_b_map,
1638 coeff_ml_a.view(),
1639 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1640 )?
1641 + &weighted_crossprod_psi_maps(
1642 CustomFamilyPsiLinearMapRef::Dense(xmu),
1643 coeff_ml_a.view(),
1644 x_ls_b_map,
1645 )?
1646 + &weighted_crossprod_psi_maps(
1647 CustomFamilyPsiLinearMapRef::Dense(xmu),
1648 coeff_ml_b.view(),
1649 x_ls_a_map,
1650 )?
1651 + &xt_diag_y_dense(xmu, &coeff_ml_ab, x_ls)?
1652 + &weighted_crossprod_psi_maps(
1653 CustomFamilyPsiLinearMapRef::Dense(xmu),
1654 coeff_ml.view(),
1655 x_ls_ab_map,
1656 )?;
1657 let hll_ab = weighted_crossprod_psi_maps(
1658 x_ls_ab_map,
1659 coeff_ll.view(),
1660 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1661 )?;
1662 let hll_ij = weighted_crossprod_psi_maps(x_ls_a_map, coeff_ll.view(), x_ls_b_map)?;
1663 let hll_iwj = weighted_crossprod_psi_maps(
1664 x_ls_a_map,
1665 coeff_ll_b.view(),
1666 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1667 )?;
1668 let hll_jwi = weighted_crossprod_psi_maps(
1669 x_ls_b_map,
1670 coeff_ll_a.view(),
1671 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1672 )?;
1673 let h_ll = &hll_ab
1674 + &hll_ab.t()
1675 + &hll_ij
1676 + hll_ij.t()
1677 + &hll_iwj
1678 + hll_iwj.t()
1679 + &hll_jwi
1680 + hll_jwi.t()
1681 + &xt_diag_x_dense(x_ls, &coeff_ll_ab)?;
1682 let h_mw = weighted_crossprod_psi_maps(
1683 xmu_ab_map,
1684 a.view(),
1685 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1686 )? + &weighted_crossprod_psi_maps(
1687 xmu_a_map,
1688 a_b.view(),
1689 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1690 )? + &weighted_crossprod_psi_maps(
1691 xmu_a_map,
1692 a.view(),
1693 CustomFamilyPsiLinearMapRef::Dense(&basis_b),
1694 )? + &weighted_crossprod_psi_maps(
1695 xmu_b_map,
1696 a_a.view(),
1697 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1698 )? + &xt_diag_y_dense(xmu, &a_ab, &geom.basis)?
1699 + &xt_diag_y_dense(xmu, &a_a, &basis_b)?
1700 + &weighted_crossprod_psi_maps(
1701 xmu_b_map,
1702 a.view(),
1703 CustomFamilyPsiLinearMapRef::Dense(&basis_a),
1704 )?
1705 + &xt_diag_y_dense(xmu, &a_b, &basis_a)?
1706 + &xt_diag_y_dense(xmu, &a, &basis_ab)?
1707 + &weighted_crossprod_psi_maps(
1708 xmu_ab_map,
1709 c.view(),
1710 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1711 )?
1712 + &weighted_crossprod_psi_maps(
1713 xmu_a_map,
1714 c_b.view(),
1715 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1716 )?
1717 + &weighted_crossprod_psi_maps(
1718 xmu_a_map,
1719 c.view(),
1720 CustomFamilyPsiLinearMapRef::Dense(&basis1_b),
1721 )?
1722 + &weighted_crossprod_psi_maps(
1723 xmu_b_map,
1724 c_a.view(),
1725 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
1726 )?
1727 + &xt_diag_y_dense(xmu, &c_ab, &geom.basis_d1)?
1728 + &xt_diag_y_dense(xmu, &c_a, &basis1_b)?
1729 + &weighted_crossprod_psi_maps(
1730 xmu_b_map,
1731 c.view(),
1732 CustomFamilyPsiLinearMapRef::Dense(&basis1_a),
1733 )?
1734 + &xt_diag_y_dense(xmu, &c_b, &basis1_a)?
1735 + &xt_diag_y_dense(xmu, &c, &basis1_ab)?;
1736 let h_lw = weighted_crossprod_psi_maps(
1737 x_ls_ab_map,
1738 l.view(),
1739 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1740 )? + &weighted_crossprod_psi_maps(
1741 x_ls_a_map,
1742 l_b.view(),
1743 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1744 )? + &weighted_crossprod_psi_maps(
1745 x_ls_a_map,
1746 l.view(),
1747 CustomFamilyPsiLinearMapRef::Dense(&basis_b),
1748 )? + &weighted_crossprod_psi_maps(
1749 x_ls_b_map,
1750 l_a.view(),
1751 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1752 )? + &xt_diag_y_dense(x_ls, &l_ab, &geom.basis)?
1753 + &xt_diag_y_dense(x_ls, &l_a, &basis_b)?
1754 + &weighted_crossprod_psi_maps(
1755 x_ls_b_map,
1756 l.view(),
1757 CustomFamilyPsiLinearMapRef::Dense(&basis_a),
1758 )?
1759 + &xt_diag_y_dense(x_ls, &l_b, &basis_a)?
1760 + &xt_diag_y_dense(x_ls, &l, &basis_ab)?;
1761 let hww_ab = xt_diag_y_dense(&basis_ab, &rows.w, &geom.basis)?;
1762 let hww_ij = xt_diag_y_dense(&basis_a, &rows.w, &basis_b)?;
1763 let hww_iwj = xt_diag_y_dense(&basis_a, &dw_b, &geom.basis)?;
1764 let hww_jwi = xt_diag_y_dense(&basis_b, &dw_a, &geom.basis)?;
1765 let h_ww = &hww_ab
1766 + &hww_ab.t()
1767 + &hww_ij
1768 + hww_ij.t()
1769 + &hww_iwj
1770 + hww_iwj.t()
1771 + &hww_jwi
1772 + hww_jwi.t()
1773 + &xt_diag_x_dense(&geom.basis, &dw_ab)?;
1774
1775 Ok(gam_problem::ExactNewtonJointPsiSecondOrderTerms {
1776 objective_psi_psi,
1777 score_psi_psi,
1778 hessian_psi_psi: gaussian_pack_wiggle_joint_symmetrichessian(
1779 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
1780 ),
1781 hessian_psi_psi_operator: None,
1782 })
1783 }
1784
1785 pub(crate) fn exact_newton_joint_psihessian_directional_derivative_from_designs(
1786 &self,
1787 block_states: &[ParameterBlockState],
1788 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
1789 psi_index: usize,
1790 d_beta_flat: &Array1<f64>,
1791 xmu: &Array2<f64>,
1792 x_ls: &Array2<f64>,
1793 ) -> Result<Option<Array2<f64>>, String> {
1794 let Some(dir_a) = self.exact_newton_joint_psi_direction(
1795 block_states,
1796 derivative_blocks,
1797 psi_index,
1798 xmu,
1799 x_ls,
1800 &self.policy,
1801 )?
1802 else {
1803 return Ok(None);
1804 };
1805 Ok(Some(
1806 self.exact_newton_joint_psihessian_directional_derivative_from_parts(
1807 block_states,
1808 &dir_a,
1809 d_beta_flat,
1810 xmu,
1811 x_ls,
1812 )?,
1813 ))
1814 }
1815
1816 pub(crate) fn exact_newton_joint_psihessian_directional_derivative_from_parts(
1817 &self,
1818 block_states: &[ParameterBlockState],
1819 dir_a: &LocationScaleJointPsiDirection,
1820 d_beta_flat: &Array1<f64>,
1821 xmu: &Array2<f64>,
1822 x_ls: &Array2<f64>,
1823 ) -> Result<Array2<f64>, String> {
1824 let pmu = xmu.ncols();
1825 let p_ls = x_ls.ncols();
1826 let xmu_map = dir_a.x_primary_psi.as_linear_map_ref();
1827 let x_ls_map = dir_a.x_ls_psi.as_linear_map_ref();
1828 let q0 = &block_states[Self::BLOCK_MU].eta;
1829 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
1830 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
1831 let betaw = &block_states[Self::BLOCK_WIGGLE].beta;
1832 let layout = GamlssBetaLayout::withwiggle(pmu, p_ls, betaw.len());
1833 let (umu, u_ls, uw) = layout.split_three(
1834 d_beta_flat,
1835 "GaussianLocationScaleWiggleFamily joint psi hessian directional derivative",
1836 )?;
1837 let q = q0 + etaw;
1838 let geom = self.wiggle_geometry(q0.view(), betaw.view())?;
1839 let rows = self.get_or_compute_row_scalars(&q, eta_ls)?;
1840
1841 let xi = fast_av(xmu, &umu);
1842 let zeta = fast_av(x_ls, &u_ls);
1843 let zmu_a_u = xmu_map.forward_mul(umu.view());
1844 let zls_a_u = x_ls_map.forward_mul(u_ls.view());
1845 let b1u = fast_av(&geom.basis_d1, &uw);
1846 let b2u = fast_av(&geom.basis_d2, &uw);
1847 let b3u = fast_av(&geom.basis_d3, &uw);
1848
1849 let q_u = &(&geom.dq_dq0 * &xi) + &fast_av(&geom.basis, &uw);
1850 let s1_u = &(&geom.d2q_dq02 * &xi) + &b1u;
1851 let g2_u = &(&geom.d3q_dq03 * &xi) + &b2u;
1852 let g3_u = &(&geom.d4q_dq04 * &xi) + &b3u;
1853
1854 let q_a = &geom.dq_dq0 * &dir_a.z_primary_psi;
1855 let s1_a = &geom.d2q_dq02 * &dir_a.z_primary_psi;
1856 let g2_a = &geom.d3q_dq03 * &dir_a.z_primary_psi;
1857 let q_a_u = &(&s1_u * &dir_a.z_primary_psi) + &(&geom.dq_dq0 * &zmu_a_u);
1858 let s1_a_u = &(&g2_u * &dir_a.z_primary_psi) + &(&geom.d2q_dq02 * &zmu_a_u);
1859 let g2_a_u = &(&g3_u * &dir_a.z_primary_psi) + &(&geom.d3q_dq03 * &zmu_a_u);
1860
1861 let basis_u = scale_matrix_rows(&geom.basis_d1, &xi)?;
1862 let basis1_u = scale_matrix_rows(&geom.basis_d2, &xi)?;
1863 let basis_a = scale_matrix_rows(&geom.basis_d1, &dir_a.z_primary_psi)?;
1864 let basis1_a = scale_matrix_rows(&geom.basis_d2, &dir_a.z_primary_psi)?;
1865 let basis_a_u = scale_matrix_rows(&geom.basis_d2, &(&xi * &dir_a.z_primary_psi))?
1866 + &scale_matrix_rows(&geom.basis_d1, &zmu_a_u)?;
1867 let basis1_a_u = scale_matrix_rows(&geom.basis_d3, &(&xi * &dir_a.z_primary_psi))?
1868 + &scale_matrix_rows(&geom.basis_d2, &zmu_a_u)?;
1869
1870 let e_a = &dir_a.z_ls_psi;
1873 let four_k2_minus_2kpi = 4.0 * &rows.kappa * &rows.kappa - 2.0 * &rows.kappa_prime;
1874 let dw_u = -2.0 * &rows.w * &rows.kappa * ζ
1875 let dm_u = -(&rows.w * &q_u) - &(2.0 * &rows.m * &rows.kappa * &zeta);
1876 let dw_a = -2.0 * &rows.w * &rows.kappa * e_a;
1877 let dm_a = -(&rows.w * &q_a) - &(2.0 * &rows.m * &rows.kappa * e_a);
1878 let dw_a_u = &four_k2_minus_2kpi * &rows.w * &(e_a * &zeta)
1879 - &(2.0 * &rows.w * &rows.kappa * &zls_a_u);
1880 let dm_a_u = &(2.0 * &rows.w * &rows.kappa * &(&q_a * &zeta + &q_u * e_a))
1881 - &(&rows.w * &q_a_u)
1882 + &(&four_k2_minus_2kpi * &rows.m * &(e_a * &zeta))
1883 - &(2.0 * &rows.m * &rows.kappa * &zls_a_u);
1884
1885 let coeff_mm_u = &(&dw_u * &geom.dq_dq0.mapv(|v| v * v))
1886 + &(2.0 * &rows.w * &geom.dq_dq0 * &s1_u)
1887 - &(&dm_u * &geom.d2q_dq02)
1888 - &(&rows.m * &g2_u);
1889 let d = &geom.dq_dq0;
1894 let amn = &rows.obs_weight - &rows.n;
1895 let a_coef = 2.0 * &rows.kappa * &rows.kappa - &rows.kappa_prime;
1896 let ktp = &(&rows.kappa_dprime * &(1.0 - 2.0 * &rows.kappa))
1897 - &(2.0 * &rows.kappa_prime * &rows.kappa_prime);
1898 let four_kkp_minus_kdp = 4.0 * &rows.kappa * &rows.kappa_prime - &rows.kappa_dprime;
1899 let dn_u = -(2.0 * &rows.m * &q_u) - &(2.0 * &rows.n * &rows.kappa * &zeta);
1900 let dn_a = -(2.0 * &rows.m * &q_a) - &(2.0 * &rows.n * &rows.kappa * e_a);
1901 let dn_a_u = &(&(&(2.0 * &rows.w * &(&q_a * &q_u))
1902 + &(4.0 * &rows.m * &rows.kappa * &(&(&q_a * &zeta) + &(&q_u * e_a))))
1903 - &(2.0 * &rows.m * &q_a_u))
1904 + &(&(&four_k2_minus_2kpi * &rows.n * &(e_a * &zeta))
1905 - &(2.0 * &rows.n * &rows.kappa * &zls_a_u));
1906 let coeff_ml_u = &(2.0 * &rows.kappa_prime * &zeta * &rows.m * d)
1908 + &(&(2.0 * &rows.kappa * &dm_u * d) + &(2.0 * &rows.kappa * &rows.m * &s1_u));
1909 let coeff_ll_u = &(&(&rows.kappa_dprime * &zeta * &amn)
1911 + &(4.0 * &rows.kappa * &rows.kappa_prime * &zeta * &rows.n))
1912 + &(&a_coef * &dn_u);
1913 let coeff_mm_a_u = &(&dw_a_u * &geom.dq_dq0.mapv(|v| v * v))
1914 + &(2.0 * &dw_a * &geom.dq_dq0 * &s1_u)
1915 + &(2.0 * &dw_u * &geom.dq_dq0 * &s1_a)
1916 + &(2.0 * &rows.w * &s1_u * &s1_a)
1917 + &(2.0 * &rows.w * &geom.dq_dq0 * &s1_a_u)
1918 - &(&dm_a_u * &geom.d2q_dq02)
1919 - &(&dm_a * &g2_u)
1920 - &(&dm_u * &g2_a)
1921 - &(&rows.m * &g2_a_u);
1922 let coeff_ml_a_u = 2.0
1925 * &(&(&(&(&(&(&rows.kappa_dprime * &(e_a * &zeta) * &rows.m * d)
1926 + &(&rows.kappa_prime * &zls_a_u * &rows.m * d))
1927 + &(&rows.kappa_prime * d * &(&(e_a * &dm_u) + &(&zeta * &dm_a))))
1928 + &(&rows.kappa_prime * &rows.m * &(&(e_a * &s1_u) + &(&zeta * &s1_a))))
1929 + &(&rows.kappa * d * &dm_a_u))
1930 + &(&rows.kappa * &(&(&dm_a * &s1_u) + &(&dm_u * &s1_a))))
1931 + &(2.0 * &rows.kappa * &rows.m * &s1_a_u);
1932 let coeff_ll_a_u = &(&(&(&(&ktp * &amn * &(e_a * &zeta))
1935 + &(4.0
1936 * &(&(&rows.kappa_prime * &rows.kappa_prime)
1937 + &(&rows.kappa * &rows.kappa_dprime))
1938 * &rows.n
1939 * &(e_a * &zeta)))
1940 + &(&four_kkp_minus_kdp * &(&(e_a * &dn_u) + &(&zeta * &dn_a))))
1941 + &(&a_coef * &dn_a_u))
1942 + &(&(&(&rows.kappa_dprime * &amn)
1943 + &(4.0 * &rows.kappa * &rows.kappa_prime * &rows.n))
1944 * &zls_a_u);
1945
1946 let a = &rows.w * &geom.dq_dq0;
1947 let a_u = &dw_u * &geom.dq_dq0 + &rows.w * &s1_u;
1948 let a_a = &dw_a * &geom.dq_dq0 + &rows.w * &s1_a;
1949 let a_a_u = &dw_a_u * &geom.dq_dq0 + &dw_a * &s1_u + &dw_u * &s1_a + &rows.w * &s1_a_u;
1950 let c = -&rows.m;
1951 let c_u = -&dm_u;
1952 let c_a = -&dm_a;
1953 let c_a_u = -&dm_a_u;
1954 let l = 2.0 * &rows.kappa * &rows.m;
1956 let l_u = &(2.0 * &rows.kappa_prime * &zeta * &rows.m) + &(2.0 * &rows.kappa * &dm_u);
1957 let l_a = &(2.0 * &rows.kappa_prime * e_a * &rows.m) + &(2.0 * &rows.kappa * &dm_a);
1958 let l_a_u = &(&(2.0 * &rows.kappa_dprime * &(e_a * &zeta) * &rows.m)
1959 + &(&(2.0 * &rows.kappa_prime * &zls_a_u * &rows.m)
1960 + &(2.0 * &rows.kappa_prime * &(&(e_a * &dm_u) + &(&zeta * &dm_a)))))
1961 + &(2.0 * &rows.kappa * &dm_a_u);
1962
1963 let hmm_a1 = weighted_crossprod_psi_maps(
1964 xmu_map,
1965 coeff_mm_u.view(),
1966 CustomFamilyPsiLinearMapRef::Dense(xmu),
1967 )?;
1968 let h_mm = &hmm_a1 + &hmm_a1.t() + &xt_diag_x_dense(xmu, &coeff_mm_a_u)?;
1969 let h_ml = weighted_crossprod_psi_maps(
1970 xmu_map,
1971 coeff_ml_u.view(),
1972 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1973 )? + &weighted_crossprod_psi_maps(
1974 CustomFamilyPsiLinearMapRef::Dense(xmu),
1975 coeff_ml_u.view(),
1976 x_ls_map,
1977 )? + &xt_diag_y_dense(xmu, &coeff_ml_a_u, x_ls)?;
1978 let hll_a1 = weighted_crossprod_psi_maps(
1979 x_ls_map,
1980 coeff_ll_u.view(),
1981 CustomFamilyPsiLinearMapRef::Dense(x_ls),
1982 )?;
1983 let h_ll = &hll_a1 + &hll_a1.t() + &xt_diag_x_dense(x_ls, &coeff_ll_a_u)?;
1984 let h_mw = weighted_crossprod_psi_maps(
1985 xmu_map,
1986 a_u.view(),
1987 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
1988 )? + &weighted_crossprod_psi_maps(
1989 xmu_map,
1990 a.view(),
1991 CustomFamilyPsiLinearMapRef::Dense(&basis_u),
1992 )? + &xt_diag_y_dense(xmu, &a_a_u, &geom.basis)?
1993 + &xt_diag_y_dense(xmu, &a_a, &basis_u)?
1994 + &xt_diag_y_dense(xmu, &a_u, &basis_a)?
1995 + &xt_diag_y_dense(xmu, &a, &basis_a_u)?
1996 + &weighted_crossprod_psi_maps(
1997 xmu_map,
1998 c_u.view(),
1999 CustomFamilyPsiLinearMapRef::Dense(&geom.basis_d1),
2000 )?
2001 + &weighted_crossprod_psi_maps(
2002 xmu_map,
2003 c.view(),
2004 CustomFamilyPsiLinearMapRef::Dense(&basis1_u),
2005 )?
2006 + &xt_diag_y_dense(xmu, &c_a_u, &geom.basis_d1)?
2007 + &xt_diag_y_dense(xmu, &c_a, &basis1_u)?
2008 + &xt_diag_y_dense(xmu, &c_u, &basis1_a)?
2009 + &xt_diag_y_dense(xmu, &c, &basis1_a_u)?;
2010 let h_lw = weighted_crossprod_psi_maps(
2011 x_ls_map,
2012 l_u.view(),
2013 CustomFamilyPsiLinearMapRef::Dense(&geom.basis),
2014 )? + &weighted_crossprod_psi_maps(
2015 x_ls_map,
2016 l.view(),
2017 CustomFamilyPsiLinearMapRef::Dense(&basis_u),
2018 )? + &xt_diag_y_dense(x_ls, &l_a_u, &geom.basis)?
2019 + &xt_diag_y_dense(x_ls, &l_a, &basis_u)?
2020 + &xt_diag_y_dense(x_ls, &l_u, &basis_a)?
2021 + &xt_diag_y_dense(x_ls, &l, &basis_a_u)?;
2022 let hww_a_u = xt_diag_y_dense(&basis_a_u, &rows.w, &geom.basis)?;
2023 let hww_aw = xt_diag_y_dense(&basis_a, &dw_u, &geom.basis)?;
2024 let hww_au = xt_diag_y_dense(&basis_a, &rows.w, &basis_u)?;
2025 let h_ww = &hww_a_u
2026 + &hww_a_u.t()
2027 + &hww_aw
2028 + hww_aw.t()
2029 + &hww_au
2030 + hww_au.t()
2031 + &xt_diag_x_dense(&geom.basis, &dw_a_u)?;
2032
2033 Ok(gaussian_pack_wiggle_joint_symmetrichessian(
2034 &h_mm, &h_ml, &h_mw, &h_ll, &h_lw, &h_ww,
2035 ))
2036 }
2037
2038 pub(crate) fn exact_newton_joint_psi_terms_for_specs(
2039 &self,
2040 block_states: &[ParameterBlockState],
2041 specs: &[ParameterBlockSpec],
2042 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
2043 psi_index: usize,
2044 ) -> Result<Option<gam_problem::ExactNewtonJointPsiTerms>, String> {
2045 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
2046 return Ok(None);
2047 };
2048 self.exact_newton_joint_psi_terms_from_designs(
2049 block_states,
2050 derivative_blocks,
2051 psi_index,
2052 &xmu,
2053 &x_ls,
2054 )
2055 }
2056
2057 pub(crate) fn exact_newton_joint_psisecond_order_terms_for_specs(
2058 &self,
2059 block_states: &[ParameterBlockState],
2060 specs: &[ParameterBlockSpec],
2061 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
2062 psi_i: usize,
2063 psi_j: usize,
2064 ) -> Result<Option<gam_problem::ExactNewtonJointPsiSecondOrderTerms>, String> {
2065 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
2066 return Ok(None);
2067 };
2068 self.exact_newton_joint_psisecond_order_terms_from_designs(
2069 block_states,
2070 derivative_blocks,
2071 psi_i,
2072 psi_j,
2073 &xmu,
2074 &x_ls,
2075 )
2076 }
2077
2078 pub(crate) fn exact_newton_joint_psihessian_directional_derivative_for_specs(
2079 &self,
2080 block_states: &[ParameterBlockState],
2081 specs: &[ParameterBlockSpec],
2082 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
2083 psi_index: usize,
2084 d_beta_flat: &Array1<f64>,
2085 ) -> Result<Option<Array2<f64>>, String> {
2086 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
2087 return Ok(None);
2088 };
2089 self.exact_newton_joint_psihessian_directional_derivative_from_designs(
2090 block_states,
2091 derivative_blocks,
2092 psi_index,
2093 d_beta_flat,
2094 &xmu,
2095 &x_ls,
2096 )
2097 }
2098}
2099
2100impl CustomFamily for GaussianLocationScaleWiggleFamily {
2101 fn joint_jeffreys_term_required(&self) -> bool {
2105 true
2106 }
2107
2108 fn exact_newton_joint_hessian_beta_dependent(&self) -> bool {
2109 true
2110 }
2111
2112 fn outer_seed_config(&self, n_params: usize) -> crate::seeding::SeedConfig {
2120 if n_params == 0 {
2121 return crate::seeding::SeedConfig::default();
2122 }
2123 let mut config = crate::seeding::SeedConfig::default();
2124 config.risk_profile = crate::seeding::SeedRiskProfile::GaussianLocationScale;
2125 config.max_seeds = 4;
2126 config.seed_budget = 2;
2127 config
2128 }
2129
2130 fn coefficient_hessian_cost(&self, specs: &[ParameterBlockSpec]) -> u64 {
2131 crate::location_scale_engine::location_scale_coefficient_hessian_cost(
2136 self.y.len() as u64,
2137 specs,
2138 )
2139 }
2140
2141 fn block_linear_constraints(
2142 &self,
2143 _: &[ParameterBlockState],
2144 block_idx: usize,
2145 spec: &ParameterBlockSpec,
2146 ) -> Result<Option<LinearInequalityConstraints>, String> {
2147 if block_idx != Self::BLOCK_WIGGLE {
2148 return Ok(None);
2149 }
2150 Ok(monotone_wiggle_nonnegative_constraints(spec.design.ncols()))
2151 }
2152
2153 fn post_update_block_beta(
2154 &self,
2155 _: &[ParameterBlockState],
2156 block_idx: usize,
2157 block_spec: &ParameterBlockSpec,
2158 beta: Array1<f64>,
2159 ) -> Result<Array1<f64>, String> {
2160 assert!(!block_spec.name.is_empty());
2161 if block_idx != Self::BLOCK_WIGGLE {
2162 return Ok(beta);
2163 }
2164 let beta = project_monotone_wiggle_beta_nonnegative(beta);
2165 validate_monotone_wiggle_beta_nonnegative(
2166 &beta,
2167 "GaussianLocationScaleWiggleFamily post-update",
2168 )?;
2169 Ok(beta)
2170 }
2171
2172 fn evaluate(&self, block_states: &[ParameterBlockState]) -> Result<FamilyEvaluation, String> {
2173 validate_block_count::<GamlssError>(
2174 "GaussianLocationScaleWiggleFamily",
2175 3,
2176 block_states.len(),
2177 )?;
2178 let n = self.y.len();
2179 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2180 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
2181 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
2182 if eta_mu.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
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 zmu = Array1::<f64>::zeros(n);
2196 let mut wmu = Array1::<f64>::zeros(n);
2197 let mut zls = Array1::<f64>::zeros(n);
2198 let mut wls = Array1::<f64>::zeros(n);
2199 let mut zw = Array1::<f64>::zeros(n);
2200 let mut ww = Array1::<f64>::zeros(n);
2201 const CHUNK: usize = 1024;
2202 let zmu_s = zmu
2203 .as_slice_memory_order_mut()
2204 .expect("zeros is contiguous");
2205 let wmu_s = wmu
2206 .as_slice_memory_order_mut()
2207 .expect("zeros is contiguous");
2208 let zls_s = zls
2209 .as_slice_memory_order_mut()
2210 .expect("zeros is contiguous");
2211 let wls_s = wls
2212 .as_slice_memory_order_mut()
2213 .expect("zeros is contiguous");
2214 let zw_s = zw.as_slice_memory_order_mut().expect("zeros is contiguous");
2215 let ww_s = ww.as_slice_memory_order_mut().expect("zeros is contiguous");
2216 let y_view = self.y.view();
2217 let w_view = self.weights.view();
2218 let eta_mu_view = eta_mu.view();
2219 let eta_ls_view = eta_ls.view();
2220 let etaw_view = etaw.view();
2221 let ll: f64 = zmu_s
2222 .par_chunks_mut(CHUNK)
2223 .zip(wmu_s.par_chunks_mut(CHUNK))
2224 .zip(zls_s.par_chunks_mut(CHUNK))
2225 .zip(wls_s.par_chunks_mut(CHUNK))
2226 .zip(zw_s.par_chunks_mut(CHUNK))
2227 .zip(ww_s.par_chunks_mut(CHUNK))
2228 .enumerate()
2229 .map(
2230 |(chunk_idx, (((((zmu_c, wmu_c), zls_c), wls_c), zw_c), ww_c))| {
2231 let start = chunk_idx * CHUNK;
2232 let mut local_ll = 0.0;
2233 for local in 0..zmu_c.len() {
2234 let i = start + local;
2235 let q_i = eta_mu_view[i] + etaw_view[i];
2236 let row = gaussian_diagonal_row_kernel(
2237 y_view[i],
2238 q_i,
2239 eta_ls_view[i],
2240 w_view[i],
2241 ln2pi,
2242 );
2243 let w_i = row.location_working_weight;
2244 let shift = row.location_working_shift;
2245 zmu_c[local] = eta_mu_view[i] + shift;
2246 wmu_c[local] = w_i;
2247 zw_c[local] = etaw_view[i] + shift;
2248 ww_c[local] = w_i;
2249 zls_c[local] = row.log_sigma_working_response;
2250 wls_c[local] = row.log_sigma_working_weight;
2251 local_ll += row.log_likelihood;
2252 }
2253 local_ll
2254 },
2255 )
2256 .sum();
2257
2258 Ok(FamilyEvaluation {
2259 log_likelihood: ll,
2260 blockworking_sets: vec![
2261 BlockWorkingSet::diagonal_checked(zmu, wmu)?,
2262 BlockWorkingSet::diagonal_checked(zls, wls)?,
2263 BlockWorkingSet::diagonal_checked(zw, ww)?,
2264 ],
2265 })
2266 }
2267
2268 fn log_likelihood_only(&self, block_states: &[ParameterBlockState]) -> Result<f64, String> {
2269 validate_block_count::<GamlssError>(
2270 "GaussianLocationScaleWiggleFamily",
2271 3,
2272 block_states.len(),
2273 )?;
2274 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2275 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
2276 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
2277 if eta_mu.len() != self.y.len()
2278 || eta_ls.len() != self.y.len()
2279 || etaw.len() != self.y.len()
2280 || self.weights.len() != self.y.len()
2281 {
2282 return Err(GamlssError::DimensionMismatch {
2283 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
2284 }
2285 .into());
2286 }
2287 let q = eta_mu + etaw;
2288 let ln2pi = (2.0 * std::f64::consts::PI).ln();
2289 let mut ll = 0.0;
2290 for i in 0..self.y.len() {
2291 let sigma_i = logb_sigma_from_eta_scalar(eta_ls[i]);
2292 let inv_s2 = (sigma_i * sigma_i).recip();
2293 let r = self.y[i] - q[i];
2294 ll += self.weights[i] * (-0.5 * (r * r * inv_s2 + ln2pi + 2.0 * sigma_i.ln()));
2295 }
2296 Ok(ll)
2297 }
2298
2299 fn log_likelihood_only_with_options(
2310 &self,
2311 block_states: &[ParameterBlockState],
2312 options: &BlockwiseFitOptions,
2313 ) -> Result<f64, String> {
2314 let Some(subsample) = options.outer_score_subsample.as_ref() else {
2315 return self.log_likelihood_only(block_states);
2316 };
2317 validate_block_count::<GamlssError>(
2318 "GaussianLocationScaleWiggleFamily",
2319 3,
2320 block_states.len(),
2321 )?;
2322 let n = self.y.len();
2323 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2324 let eta_ls = &block_states[Self::BLOCK_LOG_SIGMA].eta;
2325 let etaw = &block_states[Self::BLOCK_WIGGLE].eta;
2326 if eta_mu.len() != n || eta_ls.len() != n || etaw.len() != n || self.weights.len() != n {
2327 return Err(GamlssError::DimensionMismatch {
2328 reason: "GaussianLocationScaleWiggleFamily input size mismatch".to_string(),
2329 }
2330 .into());
2331 }
2332 let ln2pi = (2.0 * std::f64::consts::PI).ln();
2333 use rayon::iter::ParallelIterator;
2334 let ll: f64 = subsample
2335 .rows
2336 .par_iter()
2337 .map(|row| {
2338 let i = row.index;
2339 let wi = self.weights[i];
2340 if wi == 0.0 {
2341 return 0.0;
2342 }
2343 let sigma_i = logb_sigma_from_eta_scalar(eta_ls[i]);
2344 let inv_s2 = (sigma_i * sigma_i).recip();
2345 let r = self.y[i] - eta_mu[i] - etaw[i];
2346 row.weight * wi * (-0.5 * (r * r * inv_s2 + ln2pi + 2.0 * sigma_i.ln()))
2347 })
2348 .sum();
2349 Ok(ll)
2350 }
2351
2352 fn requires_joint_outer_hyper_path(&self) -> bool {
2353 true
2354 }
2355
2356 fn exact_newton_hessian_directional_derivative(
2357 &self,
2358 block_states: &[ParameterBlockState],
2359 block_idx: usize,
2360 d_beta: &Array1<f64>,
2361 ) -> Result<Option<Array2<f64>>, String> {
2362 validate_block_count::<GamlssError>(
2363 "GaussianLocationScaleWiggleFamily",
2364 3,
2365 block_states.len(),
2366 )?;
2367 let pmu = self
2368 .mu_design
2369 .as_ref()
2370 .ok_or_else(|| {
2371 "GaussianLocationScaleWiggleFamily exact path is missing mu design".to_string()
2372 })?
2373 .ncols();
2374 let p_ls = self
2375 .log_sigma_design
2376 .as_ref()
2377 .ok_or_else(|| {
2378 "GaussianLocationScaleWiggleFamily exact path is missing log-sigma design"
2379 .to_string()
2380 })?
2381 .ncols();
2382 let pw = block_states[Self::BLOCK_WIGGLE].beta.len();
2383 let total = pmu + p_ls + pw;
2384 let (start, end) = match block_idx {
2385 Self::BLOCK_MU => (0usize, pmu),
2386 Self::BLOCK_LOG_SIGMA => (pmu, pmu + p_ls),
2387 Self::BLOCK_WIGGLE => (pmu + p_ls, total),
2388 _ => return Ok(None),
2389 };
2390 if d_beta.len() != end - start {
2391 return Err(GamlssError::DimensionMismatch { reason: format!(
2392 "GaussianLocationScaleWiggleFamily block {block_idx} d_beta length mismatch: got {}, expected {}",
2393 d_beta.len(),
2394 end - start
2395 ) }.into());
2396 }
2397 let mut d_beta_flat = Array1::<f64>::zeros(total);
2398 d_beta_flat.slice_mut(s![start..end]).assign(d_beta);
2399 let (xmu, x_ls) = self.dense_block_designs()?;
2400 let d_joint = self
2401 .exact_newton_joint_hessian_directional_derivative_from_designs(
2402 block_states,
2403 &xmu,
2404 &x_ls,
2405 &d_beta_flat,
2406 )?
2407 .ok_or_else(|| "missing Gaussian wiggle exact joint directional Hessian".to_string())?;
2408 Ok(Some(d_joint.slice(s![start..end, start..end]).to_owned()))
2409 }
2410
2411 fn exact_newton_joint_hessian(
2412 &self,
2413 block_states: &[ParameterBlockState],
2414 ) -> Result<Option<Array2<f64>>, String> {
2415 self.exact_newton_joint_hessian_for_specs(block_states, None)
2416 }
2417
2418 fn has_explicit_joint_hessian(&self) -> bool {
2419 true
2420 }
2421
2422 fn exact_newton_joint_hessian_directional_derivative(
2423 &self,
2424 block_states: &[ParameterBlockState],
2425 d_beta_flat: &Array1<f64>,
2426 ) -> Result<Option<Array2<f64>>, String> {
2427 self.exact_newton_joint_hessian_directional_derivative_for_specs(
2428 block_states,
2429 None,
2430 d_beta_flat,
2431 )
2432 }
2433
2434 fn exact_newton_joint_hessiansecond_directional_derivative(
2435 &self,
2436 block_states: &[ParameterBlockState],
2437 d_beta_u_flat: &Array1<f64>,
2438 d_beta_v_flat: &Array1<f64>,
2439 ) -> Result<Option<Array2<f64>>, String> {
2440 self.exact_newton_joint_hessian_second_directional_derivative_for_specs(
2441 block_states,
2442 None,
2443 d_beta_u_flat,
2444 d_beta_v_flat,
2445 )
2446 }
2447
2448 fn exact_newton_joint_hessian_with_specs(
2449 &self,
2450 block_states: &[ParameterBlockState],
2451 specs: &[ParameterBlockSpec],
2452 ) -> Result<Option<Array2<f64>>, String> {
2453 self.exact_newton_joint_hessian_for_specs(block_states, Some(specs))
2454 }
2455
2456 fn exact_newton_joint_hessian_directional_derivative_with_specs(
2457 &self,
2458 block_states: &[ParameterBlockState],
2459 specs: &[ParameterBlockSpec],
2460 d_beta_flat: &Array1<f64>,
2461 ) -> Result<Option<Array2<f64>>, String> {
2462 self.exact_newton_joint_hessian_directional_derivative_for_specs(
2463 block_states,
2464 Some(specs),
2465 d_beta_flat,
2466 )
2467 }
2468
2469 fn exact_newton_joint_hessian_second_directional_derivative_with_specs(
2470 &self,
2471 block_states: &[ParameterBlockState],
2472 specs: &[ParameterBlockSpec],
2473 d_beta_u_flat: &Array1<f64>,
2474 d_beta_v_flat: &Array1<f64>,
2475 ) -> Result<Option<Array2<f64>>, String> {
2476 self.exact_newton_joint_hessian_second_directional_derivative_for_specs(
2477 block_states,
2478 Some(specs),
2479 d_beta_u_flat,
2480 d_beta_v_flat,
2481 )
2482 }
2483
2484 fn exact_newton_joint_psi_terms(
2485 &self,
2486 block_states: &[ParameterBlockState],
2487 specs: &[ParameterBlockSpec],
2488 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
2489 psi_index: usize,
2490 ) -> Result<Option<gam_problem::ExactNewtonJointPsiTerms>, String> {
2491 self.exact_newton_joint_psi_terms_for_specs(
2492 block_states,
2493 specs,
2494 derivative_blocks,
2495 psi_index,
2496 )
2497 }
2498
2499 fn exact_newton_joint_psisecond_order_terms(
2500 &self,
2501 block_states: &[ParameterBlockState],
2502 specs: &[ParameterBlockSpec],
2503 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
2504 psi_i: usize,
2505 psi_j: usize,
2506 ) -> Result<Option<gam_problem::ExactNewtonJointPsiSecondOrderTerms>, String> {
2507 self.exact_newton_joint_psisecond_order_terms_for_specs(
2508 block_states,
2509 specs,
2510 derivative_blocks,
2511 psi_i,
2512 psi_j,
2513 )
2514 }
2515
2516 fn exact_newton_joint_psihessian_directional_derivative(
2517 &self,
2518 block_states: &[ParameterBlockState],
2519 specs: &[ParameterBlockSpec],
2520 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
2521 psi_index: usize,
2522 d_beta_flat: &Array1<f64>,
2523 ) -> Result<Option<Array2<f64>>, String> {
2524 self.exact_newton_joint_psihessian_directional_derivative_for_specs(
2525 block_states,
2526 specs,
2527 derivative_blocks,
2528 psi_index,
2529 d_beta_flat,
2530 )
2531 }
2532
2533 fn exact_newton_joint_psi_workspace(
2534 &self,
2535 block_states: &[ParameterBlockState],
2536 specs: &[ParameterBlockSpec],
2537 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
2538 ) -> Result<Option<Arc<dyn ExactNewtonJointPsiWorkspace>>, String> {
2539 if !self.exact_joint_supported() {
2540 return Ok(None);
2541 }
2542 Ok(Some(Arc::new(
2543 GaussianLocationScaleWiggleExactNewtonJointPsiWorkspace::new(
2544 self.clone(),
2545 block_states.to_vec(),
2546 specs,
2547 derivative_blocks.to_vec(),
2548 )?,
2549 )))
2550 }
2551
2552 fn exact_newton_joint_psi_workspace_with_options(
2569 &self,
2570 block_states: &[ParameterBlockState],
2571 specs: &[ParameterBlockSpec],
2572 derivative_blocks: &[Vec<crate::custom_family::CustomFamilyBlockPsiDerivative>],
2573 options: &BlockwiseFitOptions,
2574 ) -> Result<Option<Arc<dyn ExactNewtonJointPsiWorkspace>>, String> {
2575 if !self.exact_joint_supported() {
2576 return Ok(None);
2577 }
2578 Ok(Some(Arc::new(
2579 GaussianLocationScaleWiggleExactNewtonJointPsiWorkspace::new_with_subsample(
2580 self.clone(),
2581 block_states.to_vec(),
2582 specs,
2583 derivative_blocks.to_vec(),
2584 options.outer_score_subsample.clone(),
2585 )?,
2586 )))
2587 }
2588
2589 fn block_geometry(
2590 &self,
2591 block_states: &[ParameterBlockState],
2592 spec: &ParameterBlockSpec,
2593 ) -> Result<(DesignMatrix, Array1<f64>), String> {
2594 if spec.name != "wiggle" {
2595 return Ok((spec.design.clone(), spec.offset.clone()));
2596 }
2597 if block_states.is_empty() {
2598 return Err(GamlssError::UnsupportedConfiguration {
2599 reason: "Gaussian wiggle geometry requires mean block".to_string(),
2600 }
2601 .into());
2602 }
2603 let eta_mu = &block_states[Self::BLOCK_MU].eta;
2604 if eta_mu.len() != self.y.len() {
2605 return Err(GamlssError::DimensionMismatch {
2606 reason: "Gaussian wiggle geometry input size mismatch".to_string(),
2607 }
2608 .into());
2609 }
2610 let x = self.wiggle_design(eta_mu.view())?;
2611 if x.ncols() != spec.design.ncols() {
2612 return Err(GamlssError::DimensionMismatch {
2613 reason: format!(
2614 "Gaussian dynamic wiggle design col mismatch: got {}, expected {}",
2615 x.ncols(),
2616 spec.design.ncols()
2617 ),
2618 }
2619 .into());
2620 }
2621 let nrows = x.nrows();
2622 Ok((
2623 DesignMatrix::Dense(gam_linalg::matrix::DenseDesignMatrix::from(x)),
2624 Array1::zeros(nrows),
2625 ))
2626 }
2627
2628 fn block_geometry_is_dynamic(&self) -> bool {
2629 true
2630 }
2631
2632 fn exact_newton_joint_hessian_workspace(
2633 &self,
2634 block_states: &[ParameterBlockState],
2635 specs: &[ParameterBlockSpec],
2636 ) -> Result<Option<Arc<dyn ExactNewtonJointHessianWorkspace>>, String> {
2637 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
2638 return Ok(None);
2639 };
2640 let workspace = GaussianLocationScaleWiggleHessianWorkspace::new(
2641 self.clone(),
2642 block_states.to_vec(),
2643 xmu.into_owned(),
2644 x_ls.into_owned(),
2645 )?;
2646 Ok(Some(Arc::new(workspace)))
2647 }
2648
2649 fn exact_newton_joint_hessian_workspace_with_options(
2669 &self,
2670 block_states: &[ParameterBlockState],
2671 specs: &[ParameterBlockSpec],
2672 options: &BlockwiseFitOptions,
2673 ) -> Result<Option<Arc<dyn ExactNewtonJointHessianWorkspace>>, String> {
2674 let Some((xmu, x_ls)) = self.exact_joint_dense_block_designs(Some(specs))? else {
2675 return Ok(None);
2676 };
2677 let mut workspace = GaussianLocationScaleWiggleHessianWorkspace::new(
2678 self.clone(),
2679 block_states.to_vec(),
2680 xmu.into_owned(),
2681 x_ls.into_owned(),
2682 )?;
2683 if let Some(subsample) = options.outer_score_subsample.as_ref() {
2684 workspace.apply_outer_subsample(subsample.rows.as_ref());
2685 }
2686 Ok(Some(Arc::new(workspace)))
2687 }
2688
2689 fn outer_derivative_subsample_capable(&self) -> bool {
2705 true
2706 }
2707
2708 fn inner_coefficient_hessian_hvp_available(&self, specs: &[ParameterBlockSpec]) -> bool {
2709 self.exact_joint_supported()
2716 && matches!(
2717 self.exact_joint_dense_block_designs(Some(specs)),
2718 Ok(Some(_))
2719 )
2720 }
2721}
2722
2723pub(crate) struct GaussianLocationScaleWiggleHessianWorkspace {
2737 pub(crate) family: GaussianLocationScaleWiggleFamily,
2738 pub(crate) block_states: Vec<ParameterBlockState>,
2739 pub(crate) xmu: Arc<Array2<f64>>,
2740 pub(crate) x_ls: Arc<Array2<f64>>,
2741 pub(crate) pieces: GaussianLocationScaleWiggleHessianRowPieces,
2742}
2743
2744impl GaussianLocationScaleWiggleHessianWorkspace {
2745 pub(crate) fn new(
2746 family: GaussianLocationScaleWiggleFamily,
2747 block_states: Vec<ParameterBlockState>,
2748 xmu: Array2<f64>,
2749 x_ls: Array2<f64>,
2750 ) -> Result<Self, String> {
2751 let pieces = family.wiggle_hessian_row_pieces(&block_states)?;
2752 Ok(Self {
2753 family,
2754 block_states,
2755 xmu: Arc::new(xmu),
2756 x_ls: Arc::new(x_ls),
2757 pieces,
2758 })
2759 }
2760
2761 pub(crate) fn apply_outer_subsample(
2776 &mut self,
2777 rows: &[crate::outer_subsample::WeightedOuterRow],
2778 ) {
2779 let n = self.pieces.coeff_mm.len();
2780 let mut mask_mm = Array1::<f64>::zeros(n);
2781 let mut mask_ml = Array1::<f64>::zeros(n);
2782 let mut mask_ll = Array1::<f64>::zeros(n);
2783 let mut mask_mw_b = Array1::<f64>::zeros(n);
2784 let mut mask_mw_d = Array1::<f64>::zeros(n);
2785 let mut mask_lw_b = Array1::<f64>::zeros(n);
2786 let mut maskww = Array1::<f64>::zeros(n);
2787 for r in rows {
2788 let i = r.index;
2789 let w = r.weight;
2790 mask_mm[i] = self.pieces.coeff_mm[i] * w;
2791 mask_ml[i] = self.pieces.coeff_ml[i] * w;
2792 mask_ll[i] = self.pieces.coeff_ll[i] * w;
2793 mask_mw_b[i] = self.pieces.coeff_mw_b[i] * w;
2794 mask_mw_d[i] = self.pieces.coeff_mw_d[i] * w;
2795 mask_lw_b[i] = self.pieces.coeff_lw_b[i] * w;
2796 maskww[i] = self.pieces.coeff_ww[i] * w;
2797 }
2798 self.pieces.coeff_mm = mask_mm;
2799 self.pieces.coeff_ml = mask_ml;
2800 self.pieces.coeff_ll = mask_ll;
2801 self.pieces.coeff_mw_b = mask_mw_b;
2802 self.pieces.coeff_mw_d = mask_mw_d;
2803 self.pieces.coeff_lw_b = mask_lw_b;
2804 self.pieces.coeff_ww = maskww;
2805 }
2806}
2807
2808impl ExactNewtonJointHessianWorkspace for GaussianLocationScaleWiggleHessianWorkspace {
2809 fn warm_up_outer_caches_for_mode(
2810 &self,
2811 eval_mode: gam_problem::EvalMode,
2812 ) -> Result<(), String> {
2813 match eval_mode {
2814 gam_problem::EvalMode::ValueOnly
2815 | gam_problem::EvalMode::ValueAndGradient
2816 | gam_problem::EvalMode::ValueGradientHessian => Ok(()),
2817 }
2818 }
2819
2820 fn hessian_dense(&self) -> Result<Option<Array2<f64>>, String> {
2821 let dense = self
2828 .pieces
2829 .assemble_dense(self.xmu.as_ref(), self.x_ls.as_ref())?;
2830 Ok(Some(dense))
2831 }
2832
2833 fn hessian_matvec_available(&self) -> bool {
2834 true
2835 }
2836
2837 fn hessian_matvec(&self, v: &Array1<f64>) -> Result<Option<Array1<f64>>, String> {
2838 let pmu = self.xmu.ncols();
2839 let p_ls = self.x_ls.ncols();
2840 let pw = self.pieces.basis.ncols();
2841 let total = pmu + p_ls + pw;
2842 if v.len() != total {
2843 return Err(GamlssError::DimensionMismatch {
2844 reason: format!(
2845 "GaussianLocationScaleWiggle matvec dimension mismatch: got {}, expected {}",
2846 v.len(),
2847 total
2848 ),
2849 }
2850 .into());
2851 }
2852 let v_mu = v.slice(s![0..pmu]);
2853 let v_ls = v.slice(s![pmu..pmu + p_ls]);
2854 let v_w = v.slice(s![pmu + p_ls..total]);
2855
2856 let u_mu = fast_av(self.xmu.as_ref(), &v_mu);
2857 let u_ls = fast_av(self.x_ls.as_ref(), &v_ls);
2858 let u_b = fast_av(&self.pieces.basis, &v_w);
2859 let u_d = fast_av(&self.pieces.basis_d1, &v_w);
2860
2861 let r_mu = &self.pieces.coeff_mm * &u_mu
2862 + &self.pieces.coeff_ml * &u_ls
2863 + &self.pieces.coeff_mw_b * &u_b
2864 + &self.pieces.coeff_mw_d * &u_d;
2865 let r_ls = &self.pieces.coeff_ml * &u_mu
2866 + &self.pieces.coeff_ll * &u_ls
2867 + &self.pieces.coeff_lw_b * &u_b;
2868 let r_b = &self.pieces.coeff_mw_b * &u_mu
2869 + &self.pieces.coeff_lw_b * &u_ls
2870 + &self.pieces.coeff_ww * &u_b;
2871 let r_d = &self.pieces.coeff_mw_d * &u_mu;
2872
2873 let out_mu = fast_atv(self.xmu.as_ref(), &r_mu);
2874 let out_ls = fast_atv(self.x_ls.as_ref(), &r_ls);
2875 let out_w = fast_atv(&self.pieces.basis, &r_b) + &fast_atv(&self.pieces.basis_d1, &r_d);
2876
2877 let mut out = Array1::<f64>::zeros(total);
2878 out.slice_mut(s![0..pmu]).assign(&out_mu);
2879 out.slice_mut(s![pmu..pmu + p_ls]).assign(&out_ls);
2880 out.slice_mut(s![pmu + p_ls..total]).assign(&out_w);
2881 Ok(Some(out))
2882 }
2883
2884 fn hessian_diagonal(&self) -> Result<Option<Array1<f64>>, String> {
2885 let pmu = self.xmu.ncols();
2886 let p_ls = self.x_ls.ncols();
2887 let pw = self.pieces.basis.ncols();
2888 let total = pmu + p_ls + pw;
2889 use rayon::iter::{IntoParallelIterator, ParallelIterator};
2891 let diag_mu: Vec<f64> = (0..pmu)
2892 .into_par_iter()
2893 .map(|j| {
2894 let col = self.xmu.column(j);
2895 col.iter()
2896 .zip(self.pieces.coeff_mm.iter())
2897 .map(|(&v, &c)| c * v * v)
2898 .sum()
2899 })
2900 .collect();
2901 let diag_ls: Vec<f64> = (0..p_ls)
2902 .into_par_iter()
2903 .map(|j| {
2904 let col = self.x_ls.column(j);
2905 col.iter()
2906 .zip(self.pieces.coeff_ll.iter())
2907 .map(|(&v, &c)| c * v * v)
2908 .sum()
2909 })
2910 .collect();
2911 let diag_w: Vec<f64> = (0..pw)
2912 .into_par_iter()
2913 .map(|j| {
2914 let col = self.pieces.basis.column(j);
2915 col.iter()
2916 .zip(self.pieces.coeff_ww.iter())
2917 .map(|(&v, &c)| c * v * v)
2918 .sum()
2919 })
2920 .collect();
2921 let mut diag = Array1::<f64>::zeros(total);
2922 for (j, v) in diag_mu.into_iter().enumerate() {
2923 diag[j] = v;
2924 }
2925 for (j, v) in diag_ls.into_iter().enumerate() {
2926 diag[pmu + j] = v;
2927 }
2928 for (j, v) in diag_w.into_iter().enumerate() {
2929 diag[pmu + p_ls + j] = v;
2930 }
2931 Ok(Some(diag))
2932 }
2933
2934 fn directional_derivative(
2935 &self,
2936 d_beta_flat: &Array1<f64>,
2937 ) -> Result<Option<Array2<f64>>, String> {
2938 self.family
2939 .exact_newton_joint_hessian_directional_derivative_from_designs(
2940 &self.block_states,
2941 self.xmu.as_ref(),
2942 self.x_ls.as_ref(),
2943 d_beta_flat,
2944 )
2945 }
2946
2947 fn directional_derivative_operator(
2948 &self,
2949 d_beta_flat: &Array1<f64>,
2950 ) -> Result<Option<Arc<dyn gam_problem::HyperOperator>>, String> {
2951 self.family.gls_wiggle_directional_operator(
2952 &self.block_states,
2953 self.xmu.clone(),
2954 self.x_ls.clone(),
2955 d_beta_flat,
2956 )
2957 }
2958
2959 fn second_directional_derivative(
2960 &self,
2961 d_beta_u_flat: &Array1<f64>,
2962 d_beta_v_flat: &Array1<f64>,
2963 ) -> Result<Option<Array2<f64>>, String> {
2964 self.family
2965 .exact_newton_joint_hessiansecond_directional_derivative_from_designs(
2966 &self.block_states,
2967 self.xmu.as_ref(),
2968 self.x_ls.as_ref(),
2969 d_beta_u_flat,
2970 d_beta_v_flat,
2971 )
2972 }
2973
2974 fn second_directional_derivative_operator(
2975 &self,
2976 d_beta_u: &Array1<f64>,
2977 d_beta_v: &Array1<f64>,
2978 ) -> Result<Option<Arc<dyn gam_problem::HyperOperator>>, String> {
2979 self.family.gls_wiggle_second_directional_operator(
2980 &self.block_states,
2981 self.xmu.clone(),
2982 self.x_ls.clone(),
2983 d_beta_u,
2984 d_beta_v,
2985 )
2986 }
2987}
2988
2989impl CustomFamilyGenerative for GaussianLocationScaleWiggleFamily {
2990 fn generativespec(
2991 &self,
2992 block_states: &[ParameterBlockState],
2993 ) -> Result<GenerativeSpec, String> {
2994 validate_block_count::<GamlssError>(
2995 "GaussianLocationScaleWiggleFamily",
2996 3,
2997 block_states.len(),
2998 )?;
2999 let eta_mu = &block_states[Self::BLOCK_MU].eta;
3000 let eta_wiggle = &block_states[Self::BLOCK_WIGGLE].eta;
3001 let eta_log_sigma = &block_states[Self::BLOCK_LOG_SIGMA].eta;
3002 let n = eta_mu.len();
3003 let mean = gamlss_rowwise_map(n, |i| eta_mu[i] + eta_wiggle[i]);
3004 let sigma = gamlss_rowwise_map(n, |i| logb_sigma_from_eta_scalar(eta_log_sigma[i]));
3005 Ok(GenerativeSpec {
3006 mean,
3007 noise: NoiseModel::Gaussian { sigma },
3008 })
3009 }
3010}
3011
3012pub(crate) fn expect_single_block<'a>(
3013 block_states: &'a [ParameterBlockState],
3014 family_name: &str,
3015) -> Result<&'a ParameterBlockState, String> {
3016 validate_block_count::<GamlssError>(family_name, 1, block_states.len())?;
3017 Ok(&block_states[0])
3018}