use crate::ols::PANEL_ROWS;
use faer::Mat;
pub(crate) const KKT_INTERIOR_MAX: f64 = 2.0;
pub(crate) fn assert_near(got: &[f64], want: &[f64], ctx: &str) {
assert_eq!(
got.len(),
want.len(),
"{ctx}: len {} vs {}",
got.len(),
want.len()
);
for (i, (&g, &w)) in got.iter().zip(want).enumerate() {
if g.is_nan() && w.is_nan() {
continue;
}
assert!(
(g - w).abs() <= 1e-12 + 1e-9 * w.abs(),
"{ctx}[{i}]: {g} vs {w}"
);
}
}
#[cfg(feature = "alloc-tests")]
pub(crate) fn alloc_test_guard() -> std::sync::MutexGuard<'static, ()> {
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
LOCK.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
#[cfg(feature = "alloc-tests")]
pub(crate) fn settle_background_allocs() {
for _ in 0..200 {
let probe = dhat::Profiler::builder().testing().build();
std::thread::sleep(std::time::Duration::from_millis(2));
let blocks = dhat::HeapStats::get().total_blocks;
drop(probe);
if blocks == 0 {
return;
}
}
}
pub(crate) struct TestWs {
pub suff_xtx: Mat<f64>,
pub suff_xty: Vec<f64>,
pub suff_yty: f64,
pub suff_sum_y: f64,
pub suff_n_rows: usize,
pub suff_xtx_work: Mat<f64>,
pub panel_x: Vec<f64>,
pub panel_y: Vec<f64>,
pub fit_betas: Vec<f64>,
pub fit_var_diag: Vec<f64>,
pub fit_t_sq: Vec<f64>,
pub fit_u_scratch: Vec<f64>,
pub fit_factor: Mat<f64>,
pub fit_rhs: Mat<f64>,
pub irls_eta: Vec<f64>,
pub irls_p: Vec<f64>,
pub irls_w: Vec<f64>,
pub irls_z: Vec<f64>,
pub irls_betas: Vec<f64>,
pub irls_betas_new: Vec<f64>,
pub irls_var_diag: Vec<f64>,
pub irls_t_sq: Vec<f64>,
pub irls_u_scratch: Vec<f64>,
pub irls_xtwx: Mat<f64>,
pub irls_xtwz: Vec<f64>,
pub irls_l: Mat<f64>,
pub irls_wx: Vec<f64>,
}
impl TestWs {
pub(crate) fn new(max_n: usize, n_predictors: usize, _max_n_clusters: usize) -> Self {
Self {
fit_betas: vec![0.0; n_predictors],
fit_var_diag: vec![0.0; n_predictors],
fit_t_sq: vec![0.0; n_predictors],
fit_u_scratch: vec![0.0; n_predictors],
fit_factor: Mat::<f64>::zeros(n_predictors, n_predictors),
fit_rhs: Mat::<f64>::zeros(max_n.max(n_predictors), 1),
suff_xtx: Mat::<f64>::zeros(n_predictors, n_predictors),
suff_xty: vec![0.0; n_predictors],
suff_yty: 0.0,
suff_sum_y: 0.0,
suff_n_rows: 0,
suff_xtx_work: Mat::<f64>::zeros(n_predictors, n_predictors),
panel_x: vec![0.0f64; PANEL_ROWS * n_predictors],
panel_y: vec![0.0f64; PANEL_ROWS],
irls_eta: vec![0.0; max_n],
irls_p: vec![0.0; max_n],
irls_w: vec![0.0; max_n],
irls_z: vec![0.0; max_n],
irls_betas: vec![0.0; n_predictors],
irls_betas_new: vec![0.0; n_predictors],
irls_var_diag: vec![0.0; n_predictors],
irls_t_sq: vec![0.0; n_predictors],
irls_u_scratch: vec![0.0; n_predictors],
irls_xtwx: Mat::<f64>::zeros(n_predictors, n_predictors),
irls_xtwz: vec![0.0; n_predictors],
irls_l: Mat::<f64>::zeros(n_predictors, n_predictors),
irls_wx: vec![0.0; max_n * n_predictors],
}
}
pub(crate) fn reset_suff_stats(&mut self) {
let p = self.suff_xty.len();
for j in 0..p {
for i in j..p {
self.suff_xtx[(i, j)] = 0.0;
}
self.suff_xty[j] = 0.0;
}
self.suff_yty = 0.0;
self.suff_sum_y = 0.0;
self.suff_n_rows = 0;
}
}
pub(crate) fn intercept_only_spec(sizing: crate::Sizing) -> crate::ModelSpec {
crate::ModelSpec {
family: crate::Family::Gaussian,
re: Some(crate::ReStructure {
sizing,
slopes: vec![],
extra_groupings: vec![],
}),
}
}
pub(crate) fn block_levels(rel: &crate::GroupingRelation) -> usize {
crate::ids::block_levels(rel)
}
pub(crate) fn model_atom(spec: &crate::ModelSpec) -> usize {
let re = spec.re.as_ref().expect("model_atom requires re: Some");
re.extra_groupings
.iter()
.fold(re.sizing.atom(), |a, g| a * block_levels(&g.relation))
}
pub(crate) fn extra_level_of_row(spec: &crate::ModelSpec, g: usize, i: usize) -> usize {
let re = spec
.re
.as_ref()
.expect("extra_level_of_row requires re: Some");
crate::ids::extra_level_of_row(re, g, i) as usize
}