2025-12-04 23:08:06 +01:00
|
|
|
//! Log-likelihood interface for MCMC
|
|
|
|
|
//!
|
|
|
|
|
//! Defines the LogLikelihood trait for target distributions.
|
|
|
|
|
|
|
|
|
|
/// Generic log-likelihood function trait
|
|
|
|
|
pub trait LogLikelihood: Send + Sync {
|
|
|
|
|
fn evaluate(&self, state: &[f64]) -> f64;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-10 18:54:32 +01:00
|
|
|
#[cfg(feature = "python-bindings")]
|
|
|
|
|
use pyo3::prelude::*;
|
|
|
|
|
|
2025-12-04 23:08:06 +01:00
|
|
|
/// Wrapper for Python callable log-likelihood
|
2025-12-10 18:54:32 +01:00
|
|
|
#[cfg(feature = "python-bindings")]
|
2025-12-04 23:08:06 +01:00
|
|
|
pub struct PyLogLikelihood {
|
|
|
|
|
func: Py<PyAny>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-10 18:54:32 +01:00
|
|
|
#[cfg(feature = "python-bindings")]
|
2025-12-04 23:08:06 +01:00
|
|
|
impl PyLogLikelihood {
|
|
|
|
|
pub fn new(func: Py<PyAny>) -> Self {
|
|
|
|
|
Self { func }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-10 18:54:32 +01:00
|
|
|
#[cfg(feature = "python-bindings")]
|
2025-12-04 23:08:06 +01:00
|
|
|
impl LogLikelihood for PyLogLikelihood {
|
|
|
|
|
fn evaluate(&self, state: &[f64]) -> f64 {
|
|
|
|
|
Python::with_gil(|py| {
|
|
|
|
|
let args = (state.to_vec(),);
|
|
|
|
|
self.func
|
|
|
|
|
.call1(py, args)
|
|
|
|
|
.and_then(|res| res.extract::<f64>(py))
|
|
|
|
|
.unwrap_or(f64::NEG_INFINITY)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
struct TestLogLikelihood;
|
2025-12-10 18:54:32 +01:00
|
|
|
|
2025-12-04 23:08:06 +01:00
|
|
|
impl LogLikelihood for TestLogLikelihood {
|
|
|
|
|
fn evaluate(&self, state: &[f64]) -> f64 {
|
|
|
|
|
// Standard normal log-likelihood
|
|
|
|
|
-0.5 * state.iter().map(|x| x.powi(2)).sum::<f64>()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_log_likelihood() {
|
|
|
|
|
let ll = TestLogLikelihood;
|
|
|
|
|
assert!(ll.evaluate(&[0.0]) > ll.evaluate(&[1.0]));
|
|
|
|
|
}
|
|
|
|
|
}
|