spectrafit_models/
split_pearson7.rs1use crate::Model;
2
3pub struct SplitPearson7;
12
13fn p7(a: f64, z: f64, m: f64) -> f64 {
14 a / (1.0 + z * z * (2.0_f64.powf(1.0 / m) - 1.0)).powf(m)
15}
16
17impl Model for SplitPearson7 {
18 fn eval(&self, x: &[f64], params: &[f64]) -> f64 {
19 let (a, c, sl, sr, ml, mr) = (
20 params[0], params[1], params[2], params[3], params[4], params[5],
21 );
22 if x[0] < c {
23 p7(a, (x[0] - c) / sl, ml)
24 } else {
25 p7(a, (x[0] - c) / sr, mr)
26 }
27 }
28
29 fn jacobian(&self, x: &[f64], params: &[f64]) -> Vec<f64> {
30 let mut p = params.to_vec();
31 (0..params.len())
32 .map(|i| {
33 let h = 1e-7_f64 * params[i].abs().max(1e-7);
34 p[i] = params[i] + h;
35 let f_plus = self.eval(x, &p);
36 p[i] = params[i] - h;
37 let f_minus = self.eval(x, &p);
38 p[i] = params[i];
39 (f_plus - f_minus) / (2.0 * h)
40 })
41 .collect()
42 }
43
44 fn param_names(&self) -> Vec<std::borrow::Cow<'static, str>> {
45 vec![
46 "amplitude".into(),
47 "center".into(),
48 "sigma_l".into(),
49 "sigma_r".into(),
50 "m_l".into(),
51 "m_r".into(),
52 ]
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use approx::assert_relative_eq;
60
61 #[test]
62 fn eval_at_center_equals_amplitude() {
63 let v = SplitPearson7.eval(&[1.5], &[3.0, 1.5, 0.6, 1.2, 2.0, 3.0]);
64 assert_relative_eq!(v, 3.0, epsilon = 1e-12);
65 }
66
67 #[test]
68 fn param_names_and_jacobian() {
69 assert_eq!(
70 SplitPearson7
71 .param_names()
72 .iter()
73 .map(|c| c.as_ref())
74 .collect::<Vec<_>>(),
75 &["amplitude", "center", "sigma_l", "sigma_r", "m_l", "m_r"]
76 );
77 let j = SplitPearson7.jacobian(&[1.0], &[3.0, 1.5, 0.6, 1.2, 2.0, 3.0]);
78 assert_eq!(j.len(), 6);
79 }
80
81 #[test]
91 fn symmetric_widths_and_exponents_equal_plain_pearson7() {
92 use crate::pearson7::Pearson7;
93 let split = SplitPearson7;
94 let plain = Pearson7;
95 let sigma = 0.7_f64;
96 let m = 2.3_f64;
97 let a = 4.2_f64;
98 let c = 0.3_f64;
99 let p_split = [a, c, sigma, sigma, m, m]; let p_plain = [a, c, sigma, m];
101 for &xi in &[-1.5_f64, -0.3, c, 0.5, 1.5] {
102 assert_relative_eq!(
103 split.eval(&[xi], &p_split),
104 plain.eval(&[xi], &p_plain),
105 epsilon = 1e-12
106 );
107 }
108 }
109}