spectrafit_models/
asym_ir.rs1use crate::Model;
2
3pub struct AsymIr;
12
13impl Model for AsymIr {
14 fn eval(&self, x: &[f64], params: &[f64]) -> f64 {
15 let (a, c, sigma, k) = (params[0], params[1], params[2], params[3]);
16 let dx = x[0] - c;
17 let g = a * (-(dx * dx) / (2.0 * sigma * sigma)).exp();
18 let arg = (-(k * dx)).min(50.0);
19 g / (1.0 + arg.exp())
20 }
21
22 fn jacobian(&self, x: &[f64], params: &[f64]) -> Vec<f64> {
23 let mut p = params.to_vec();
24 (0..params.len())
25 .map(|i| {
26 let h = 1e-7_f64 * params[i].abs().max(1e-7);
27 p[i] = params[i] + h;
28 let f_plus = self.eval(x, &p);
29 p[i] = params[i] - h;
30 let f_minus = self.eval(x, &p);
31 p[i] = params[i];
32 (f_plus - f_minus) / (2.0 * h)
33 })
34 .collect()
35 }
36
37 fn param_names(&self) -> Vec<std::borrow::Cow<'static, str>> {
38 vec![
39 "amplitude".into(),
40 "center".into(),
41 "sigma".into(),
42 "k".into(),
43 ]
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use approx::assert_relative_eq;
51
52 #[test]
53 fn eval_at_center_is_half_amplitude() {
54 let v = AsymIr.eval(&[1.5], &[4.0, 1.5, 0.8, 1.0]);
56 assert_relative_eq!(v, 2.0, epsilon = 1e-12);
57 }
58
59 #[test]
60 fn param_names_and_jacobian() {
61 assert_eq!(
62 AsymIr
63 .param_names()
64 .iter()
65 .map(|c| c.as_ref())
66 .collect::<Vec<_>>(),
67 &["amplitude", "center", "sigma", "k"]
68 );
69 assert_eq!(AsymIr.jacobian(&[1.0], &[4.0, 1.5, 0.8, 1.0]).len(), 4);
70 }
71
72 #[test]
82 fn k_zero_equals_half_gaussian() {
83 use crate::gaussian::Gaussian;
84 let asym = AsymIr;
85 let gauss = Gaussian;
86 let a = 5.0_f64;
87 let c = 0.3_f64;
88 let sigma = 0.8_f64;
89 let p_asym = [a, c, sigma, 0.0]; let p_gauss = [a / 2.0, c, sigma];
91 for &xi in &[-2.0_f64, -0.5, c, 0.5, 2.0] {
92 assert_relative_eq!(
93 asym.eval(&[xi], &p_asym),
94 gauss.eval(&[xi], &p_gauss),
95 epsilon = 1e-12
96 );
97 }
98 }
99}