1pub mod cli_harness;
4pub mod fd_checker;
5pub mod reference;
6
7use gam_linalg::matrix::{DenseDesignMatrix, DesignMatrix};
8use gam_problem::PenaltyMatrix;
9use gam_problem::block_spec::ParameterBlockSpec;
10use ndarray::{Array1, Array2, array};
11
12pub use gam_linalg::test_support::no_densify_design;
18
19pub struct BinomialLocationScaleBaseFixture {
20 pub n: usize,
21 pub y: Array1<f64>,
22 pub weights: Array1<f64>,
23 pub threshold_design: DesignMatrix,
24 pub log_sigma_design: DesignMatrix,
25 pub threshold_spec: ParameterBlockSpec,
26 pub log_sigma_spec: ParameterBlockSpec,
27}
28
29pub fn binomial_location_scale_base_fixture() -> BinomialLocationScaleBaseFixture {
30 let n = 7usize;
31 let y = Array1::from_vec(vec![0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0]);
32 let weights = Array1::from_vec(vec![1.0; n]);
33 let threshold_design =
34 DesignMatrix::Dense(DenseDesignMatrix::from(Array2::from_elem((n, 1), 1.0)));
35 let log_sigma_design =
36 DesignMatrix::Dense(DenseDesignMatrix::from(Array2::from_elem((n, 1), 1.0)));
37 let threshold_spec = ParameterBlockSpec {
38 name: "threshold".to_string(),
39 design: threshold_design.clone(),
40 offset: Array1::zeros(n),
41 penalties: vec![PenaltyMatrix::Dense(Array2::eye(1))],
42 nullspace_dims: vec![],
43 initial_log_lambdas: array![0.0],
44 initial_beta: Some(array![0.2]),
45 gauge_priority: 100,
46 jacobian_callback: None,
47 stacked_design: None,
48 stacked_offset: None,
49 };
50 let log_sigma_spec = ParameterBlockSpec {
51 name: "log_sigma".to_string(),
52 design: log_sigma_design.clone(),
53 offset: Array1::zeros(n),
54 penalties: vec![PenaltyMatrix::Dense(Array2::eye(1))],
55 nullspace_dims: vec![],
56 initial_log_lambdas: array![-0.2],
57 initial_beta: Some(array![-0.1]),
58 gauge_priority: 100,
59 jacobian_callback: None,
60 stacked_design: None,
61 stacked_offset: None,
62 };
63 BinomialLocationScaleBaseFixture {
64 n,
65 y,
66 weights,
67 threshold_design,
68 log_sigma_design,
69 threshold_spec,
70 log_sigma_spec,
71 }
72}
73
74#[macro_export]
76macro_rules! assert_central_difference_array {
77 ($x:expr, $h:expr, |$var:ident| $eval:expr, $analytical:expr, $tol:expr) => {
78 let f_plus = {
79 let $var = $x + $h;
80 $eval
81 };
82 let f_minus = {
83 let $var = $x - $h;
84 $eval
85 };
86 assert_eq!(f_plus.len(), $analytical.len());
87 for j in 0..$analytical.len() {
88 let fd = (f_plus[j] - f_minus[j]) / (2.0 * $h);
89 approx::assert_abs_diff_eq!(fd, $analytical[j], epsilon = $tol);
90 }
91 };
92}
93
94pub fn assert_matrix_derivativefd_rel(
110 fd: &Array2<f64>,
111 analytic: &Array2<f64>,
112 rel_tol: f64,
113 label: &str,
114) {
115 assert_eq!(analytic.dim(), fd.dim(), "{} dimensions must match", label);
116 for i in 0..analytic.nrows() {
117 for j in 0..analytic.ncols() {
118 let analytic_ij = analytic[[i, j]];
119 let fd_ij = fd[[i, j]];
120 let tol = rel_tol * (1.0 + analytic_ij.abs());
121 if analytic_ij.abs() > tol && fd_ij.abs() > tol {
122 assert_eq!(
123 analytic_ij.signum(),
124 fd_ij.signum(),
125 "{} sign mismatch at ({}, {}): analytic={}, fd={}",
126 label,
127 i,
128 j,
129 analytic_ij,
130 fd_ij
131 );
132 }
133 let diff = (analytic_ij - fd_ij).abs();
134 assert!(
135 diff <= tol,
136 "{} value mismatch at ({}, {}): analytic={}, fd={}, abs_diff={}, rel_tol={}, tol={}",
137 label,
138 i,
139 j,
140 analytic_ij,
141 fd_ij,
142 diff,
143 rel_tol,
144 tol
145 );
146 }
147 }
148}
149
150pub fn assert_matrix_derivativefd(fd: &Array2<f64>, analytic: &Array2<f64>, tol: f64, label: &str) {
153 assert_eq!(analytic.dim(), fd.dim(), "{} dimensions must match", label);
154 for i in 0..analytic.nrows() {
155 for j in 0..analytic.ncols() {
156 let analytic_ij = analytic[[i, j]];
157 let fd_ij = fd[[i, j]];
158 let diff = (analytic_ij - fd_ij).abs();
159
160 if analytic_ij.abs() > tol && fd_ij.abs() > tol {
161 assert_eq!(
162 analytic_ij.signum(),
163 fd_ij.signum(),
164 "{} sign mismatch at ({}, {}): analytic={}, fd={}",
165 label,
166 i,
167 j,
168 analytic_ij,
169 fd_ij
170 );
171 }
172 assert!(
173 diff <= tol,
174 "{} value mismatch at ({}, {}): analytic={}, fd={}, abs_diff={}, tol={}",
175 label,
176 i,
177 j,
178 analytic_ij,
179 fd_ij,
180 diff,
181 tol
182 );
183 }
184 }
185}
186
187pub fn spec_from_dense(
188 name: &str,
189 design: ndarray::Array2<f64>,
190) -> gam_problem::block_spec::ParameterBlockSpec {
191 let n = design.nrows();
192 gam_problem::block_spec::ParameterBlockSpec {
193 name: name.to_string(),
194 design: gam_linalg::matrix::DesignMatrix::Dense(
195 gam_linalg::matrix::DenseDesignMatrix::from(design),
196 ),
197 offset: ndarray::Array1::<f64>::zeros(n),
198 penalties: Vec::new(),
199 nullspace_dims: Vec::new(),
200 initial_log_lambdas: ndarray::Array1::<f64>::zeros(0),
201 initial_beta: None,
202 gauge_priority: 100,
203 jacobian_callback: None,
204 stacked_design: None,
205 stacked_offset: None,
206 }
207}
208
209pub fn spec_from_dense_with_priority(
210 name: &str,
211 design: ndarray::Array2<f64>,
212 priority: u8,
213) -> gam_problem::block_spec::ParameterBlockSpec {
214 let mut s = spec_from_dense(name, design);
215 s.gauge_priority = priority;
216 s
217}