use scirs2_core::ndarray::{Array1, Array2};
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum LikelihoodFamily {
Gaussian,
Poisson,
Binomial,
NegativeBinomial,
}
#[derive(Debug, Clone)]
pub struct LatentGaussianModel {
pub y: Array1<f64>,
pub design_matrix: Array2<f64>,
pub precision_matrix: Array2<f64>,
pub likelihood: LikelihoodFamily,
pub n_trials: Option<Array1<f64>>,
pub observation_precision: Option<f64>,
}
impl LatentGaussianModel {
pub fn new(
y: Array1<f64>,
design_matrix: Array2<f64>,
precision_matrix: Array2<f64>,
likelihood: LikelihoodFamily,
) -> Self {
Self {
y,
design_matrix,
precision_matrix,
likelihood,
n_trials: None,
observation_precision: None,
}
}
pub fn with_n_trials(mut self, n_trials: Array1<f64>) -> Self {
self.n_trials = Some(n_trials);
self
}
pub fn with_observation_precision(mut self, precision: f64) -> Self {
self.observation_precision = Some(precision);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum IntegrationStrategy {
Grid,
CCD,
SimplifiedLaplace,
}
#[derive(Debug, Clone)]
pub struct INLAConfig {
pub n_hyperparameter_grid: usize,
pub integration_strategy: IntegrationStrategy,
pub max_newton_iter: usize,
pub newton_tol: f64,
pub simplified_laplace: bool,
pub newton_damping: f64,
pub hyperparameter_range: Option<(f64, f64)>,
}
impl Default for INLAConfig {
fn default() -> Self {
Self {
n_hyperparameter_grid: 25,
integration_strategy: IntegrationStrategy::Grid,
max_newton_iter: 100,
newton_tol: 1e-8,
simplified_laplace: false,
newton_damping: 1.0,
hyperparameter_range: None,
}
}
}
#[derive(Debug, Clone)]
pub struct HyperparameterPosterior {
pub grid_points: Vec<f64>,
pub log_densities: Vec<f64>,
pub mean: f64,
pub variance: f64,
}
#[derive(Debug, Clone)]
pub struct INLAResult {
pub marginal_means: Array1<f64>,
pub marginal_variances: Array1<f64>,
pub hyperparameter_posteriors: Vec<HyperparameterPosterior>,
pub log_marginal_likelihood: f64,
pub converged: bool,
pub newton_iterations: usize,
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::{array, Array2};
#[test]
fn test_default_config() {
let config = INLAConfig::default();
assert_eq!(config.n_hyperparameter_grid, 25);
assert_eq!(config.max_newton_iter, 100);
assert!((config.newton_tol - 1e-8).abs() < 1e-15);
assert!(!config.simplified_laplace);
assert_eq!(config.integration_strategy, IntegrationStrategy::Grid);
}
#[test]
fn test_latent_gaussian_model_new() {
let y = array![1.0, 2.0, 3.0];
let design = Array2::eye(3);
let precision = Array2::eye(3);
let model =
LatentGaussianModel::new(y.clone(), design, precision, LikelihoodFamily::Gaussian);
assert_eq!(model.y, y);
assert_eq!(model.likelihood, LikelihoodFamily::Gaussian);
assert!(model.n_trials.is_none());
assert!(model.observation_precision.is_none());
}
#[test]
fn test_model_with_builders() {
let y = array![1.0, 0.0, 1.0];
let design = Array2::eye(3);
let precision = Array2::eye(3);
let n_trials = array![10.0, 10.0, 10.0];
let model = LatentGaussianModel::new(y, design, precision, LikelihoodFamily::Binomial)
.with_n_trials(n_trials.clone())
.with_observation_precision(1.0);
assert_eq!(model.likelihood, LikelihoodFamily::Binomial);
assert!(model.n_trials.is_some());
assert_eq!(model.n_trials.as_ref().map(|t| t.len()), Some(3));
assert!((model.observation_precision.unwrap_or(0.0) - 1.0).abs() < 1e-15);
}
#[test]
fn test_likelihood_variants() {
let variants = [
LikelihoodFamily::Gaussian,
LikelihoodFamily::Poisson,
LikelihoodFamily::Binomial,
LikelihoodFamily::NegativeBinomial,
];
for v in &variants {
let _ = format!("{:?}", v);
}
}
#[test]
fn test_integration_strategy_variants() {
let variants = [
IntegrationStrategy::Grid,
IntegrationStrategy::CCD,
IntegrationStrategy::SimplifiedLaplace,
];
for v in &variants {
let _ = format!("{:?}", v);
}
}
}