Skip to main content

spectrafit_models/
students_t.rs

1use crate::Model;
2
3/// Student's-t peak: `A / (1 + ((x − c)/σ)²/ν)^((ν+1)/2)`.
4///
5/// Parameters (in order): `[amplitude, center, sigma, nu]`. `amplitude` is the peak height
6/// at `x == center`; `nu` is the degrees-of-freedom (ν→∞ Gaussian, small ν heavy tails).
7/// numpy oracle identical: `A / (1 + ((x-c)/σ)**2/ν)**((ν+1)/2)`.
8pub struct StudentsT;
9
10impl Model for StudentsT {
11    fn eval(&self, x: &[f64], params: &[f64]) -> f64 {
12        let (a, c, sigma, nu) = (params[0], params[1], params[2], params[3]);
13        let z = (x[0] - c) / sigma;
14        a / (1.0 + z * z / nu).powf((nu + 1.0) / 2.0)
15    }
16
17    fn jacobian(&self, x: &[f64], params: &[f64]) -> Vec<f64> {
18        let mut p = params.to_vec();
19        (0..params.len())
20            .map(|i| {
21                let h = 1e-7_f64 * params[i].abs().max(1e-7);
22                p[i] = params[i] + h;
23                let f_plus = self.eval(x, &p);
24                p[i] = params[i] - h;
25                let f_minus = self.eval(x, &p);
26                p[i] = params[i];
27                (f_plus - f_minus) / (2.0 * h)
28            })
29            .collect()
30    }
31
32    fn param_names(&self) -> Vec<std::borrow::Cow<'static, str>> {
33        vec![
34            "amplitude".into(),
35            "center".into(),
36            "sigma".into(),
37            "nu".into(),
38        ]
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use approx::assert_relative_eq;
46
47    #[test]
48    fn eval_at_center_equals_amplitude() {
49        assert_relative_eq!(
50            StudentsT.eval(&[1.5], &[3.0, 1.5, 0.8, 3.0]),
51            3.0,
52            epsilon = 1e-12
53        );
54    }
55
56    #[test]
57    fn param_names_and_jacobian() {
58        assert_eq!(
59            StudentsT
60                .param_names()
61                .iter()
62                .map(|c| c.as_ref())
63                .collect::<Vec<_>>(),
64            &["amplitude", "center", "sigma", "nu"]
65        );
66        let j = StudentsT.jacobian(&[1.5], &[3.0, 1.5, 0.8, 3.0]);
67        assert_eq!(j.len(), 4);
68        assert_relative_eq!(j[0], 1.0, epsilon = 1e-6);
69    }
70}