1use oefpil::{
4 Algorithm, Covariance, Distribution, Logfile, Model, OefpilError, Parameter, Variable,
5};
6use rand_chacha::{ChaChaRng, rand_core::SeedableRng};
7
8#[allow(clippy::float_cmp)]
9fn main() -> Result<(), OefpilError> {
10 const MODEL: Model = Model {
12 fx: |x, p| p[1] * (x[0] + p[0]).to_radians().sin(),
13 dfdx: &[|x, p| p[1] * (x[0] + p[0]).to_radians().cos().to_radians()],
14 dfdp: &[
15 |x, p| p[1] * (x[0] + p[0]).to_radians().cos().to_radians(),
16 |x, p| (x[0] + p[0]).to_radians().sin(),
17 ],
18 implicit: false,
19 };
20 let p_mean = [8f64, 10.0];
22 let x_mean = [-60.0, -40.0, -20.0, -10.0, 10.0, 20.0, 40.0, 60.0];
23 let y_mean = x_mean.map(|x_mean| (MODEL.fx)(&[x_mean], &p_mean));
24 let x_deviation = x_mean.map(|x| x.abs() * 2e-2);
26 let y_deviation = y_mean.map(|y| y.abs() * 8e-2);
27 let coefficient = 0.5;
28 let correlation = x_deviation
29 .iter()
30 .zip(&y_deviation)
31 .map(|(x, y)| x * y * coefficient)
32 .collect::<Vec<f64>>();
33 let covariance = &Covariance::new_diagonals(8, 2)
34 .with_tile(0, 0, &x_deviation.map(|x| x * x))?
35 .with_tile(1, 1, &y_deviation.map(|y| y * y))?
36 .with_tile(0, 1, &correlation)?
37 .with_decomposition()?;
38 let seed = b"85a4d500a53d81b65255dc718d7df63a3cd3d6bb15e6f724af04a23d74cc02d3";
40 let mut csprng = ChaChaRng::from_seed(const_hex::decode_to_array(seed).unwrap());
42 let mut distribution = Distribution {
44 sample: &mut vec![0f64; x_mean.len() + y_mean.len()],
45 mean: &mut x_mean.iter().chain(&y_mean).copied().collect::<Vec<f64>>(),
46 covariance,
47 };
48 distribution.sample(&mut csprng)?;
50 let mut variable = Variable {
52 sample: distribution.sample,
53 mean: &mut [],
54 covariance,
55 };
56 let p_mean_initial = [9.5f64, 12.0];
58 let mut parameter = Parameter {
59 mean: &mut p_mean_initial.clone(),
60 deviation: &mut [0f64; 2],
61 covariance: &mut [0f64; 4],
62 correlation: &mut [0f64; 4],
63 };
64 let oefpil = Algorithm::default();
66 let report = oefpil.fit(MODEL, &mut variable, &mut parameter, Logfile::default())?;
67 let round = |value: f64| (value * 1e2).round() / 1e2;
69 assert_eq!(7, report.iterations);
70 assert_eq!(0.73, report.chi_squared_p_value().map(round)?);
71 assert_eq!(8.16, round(parameter.mean[0]));
73 assert_eq!(0.19, round(parameter.deviation[0]));
74 assert_eq!(9.95, round(parameter.mean[1]));
75 assert_eq!(0.28, round(parameter.deviation[1]));
76 assert_eq!(0.30, round(parameter.correlation[1]));
77 let samples = 10_000;
79 let mut p_sample = vec![0f64; samples * p_mean.len()];
80 let mut p_correlation = vec![0f64; samples];
81 for sample in 0..samples {
82 distribution.sample(&mut csprng)?;
83 let mut variable = Variable {
84 sample: distribution.sample,
85 mean: &mut [],
86 covariance,
87 };
88 let mut parameter = Parameter {
89 mean: &mut p_mean_initial.clone(),
90 covariance: &mut [0f64; 4],
91 deviation: &mut [0f64; 2],
92 correlation: &mut [0f64; 4],
93 };
94 oefpil.fit(MODEL, &mut variable, &mut parameter, Logfile::default())?;
95 p_sample[sample] = parameter.mean[0];
96 p_sample[sample + samples] = parameter.mean[1];
97 p_correlation[sample] = parameter.correlation[1];
98 }
99 let mean_deviation = |s: &[f64]| {
100 let n = f64::from(u32::try_from(s.len()).unwrap());
101 let m = s.iter().sum::<f64>() / n;
102 let v = s.iter().map(|s| s - m).map(|d| d * d).sum::<f64>() / (n - 1.0);
103 [m, v.sqrt()]
104 };
105 let [p_mean_0, p_deviation_0] = mean_deviation(&p_sample[..samples]);
106 let [p_mean_1, p_deviation_1] = mean_deviation(&p_sample[samples..]);
107 let [p_correlation_mean, p_correlation_deviation] = mean_deviation(&p_correlation);
108 assert_eq!(8.00, round(p_mean_0));
110 assert_eq!(0.19, round(p_deviation_0));
111 assert_eq!(10.0, round(p_mean_1));
112 assert_eq!(0.28, round(p_deviation_1));
113 assert_eq!(0.30, round(p_correlation_mean));
114 assert_eq!(0.02, round(p_correlation_deviation));
115 Ok(())
116}