bacon_sci/integrate/
mod.rs1use nalgebra::{ComplexField, RealField};
8use num_traits::{FromPrimitive, One, Zero};
9use std::f64;
10
11mod gaussian;
12pub use gaussian::*;
13mod tables;
14
15use tables::WEIGHTS_DE;
16
17fn integrate_core<N: ComplexField + FromPrimitive + Copy, F: FnMut(N::RealField) -> N>(
20 mut f: F,
21 tol: N::RealField,
22) -> Result<N, String>
23where
24 <N as ComplexField>::RealField: FromPrimitive + Copy,
25{
26 let mut error_estimate = N::RealField::one() + tol;
27 let mut num_function_evaluations = 1;
28 let mut current_delta = N::RealField::zero();
29
30 let half = N::from_f64(0.5).unwrap();
31 let one_point_nine = N::from_f64(1.9).unwrap().real();
32 let two_point_one = N::from_f64(2.1).unwrap().real();
33 let pi = N::from_f64(f64::consts::PI).unwrap();
34
35 let mut integral = pi * f(N::RealField::zero());
36
37 for &weight in &WEIGHTS_DE {
38 let new_contribution = weight
39 .iter()
40 .map(|&(w, x)| {
41 let x = N::from_f64(x).unwrap().real();
42 N::from_f64(w).unwrap() * (f(x) + f(-x))
43 })
44 .fold(N::zero(), |sum, x| sum + x);
45 num_function_evaluations += 2 * weight.len();
46
47 let previous_delta_ln = current_delta.ln();
49 current_delta = (half * integral - new_contribution).abs();
50 integral = half * integral + new_contribution;
51
52 if num_function_evaluations <= 13 {
55 continue; }
58
59 if current_delta == N::RealField::zero() {
65 error_estimate = N::RealField::zero();
66 break;
67 }
68 let r = current_delta.ln() / previous_delta_ln;
70
71 if r > one_point_nine && r < two_point_one {
72 error_estimate = current_delta * current_delta;
76 } else {
77 error_estimate = current_delta;
79 }
80
81 if error_estimate < tol {
82 break;
83 }
84 }
85
86 if error_estimate < tol {
87 Ok(integral)
88 } else {
89 Err("integrate: maximum iterations exceeded".to_owned())
90 }
91}
92
93pub fn integrate<N: ComplexField + FromPrimitive + Copy, F: FnMut(N::RealField) -> N>(
94 left: N::RealField,
95 right: N::RealField,
96 mut f: F,
97 tol: N::RealField,
98) -> Result<N, String>
99where
100 <N as ComplexField>::RealField: FromPrimitive + Copy,
101{
102 if left >= right {
103 return Err("integrate: left must be less than right".to_owned());
104 }
105 if !tol.is_sign_positive() {
106 return Err("integrate: tolerance must be positive".to_owned());
107 }
108
109 let half = N::from_f64(0.5).unwrap().real();
110
111 let scale = (right - left) * half;
112 let shift = (right + left) * half;
113 let scale_cmplx = N::from_real(scale);
114
115 let fun = |x: N::RealField| -> N {
116 let out = f(scale * x + shift);
117 if out.is_finite() {
118 out
119 } else {
120 N::zero()
121 }
122 };
123
124 Ok(integrate_core(fun, tol)? * scale_cmplx)
125}
126
127pub fn integrate_simpson<N: ComplexField + FromPrimitive + Copy, F: FnMut(N::RealField) -> N>(
133 left: N::RealField,
134 right: N::RealField,
135 mut f: F,
136 tol: N::RealField,
137 n_max: usize,
138) -> Result<N, String>
139where
140 <N as ComplexField>::RealField: FromPrimitive + Copy,
141{
142 if left >= right {
143 return Err("integrate: left must be less than right".to_owned());
144 }
145 if !tol.is_sign_positive() {
146 return Err("integrate: tolerance must be positive".to_owned());
147 }
148
149 let sixth = N::from_f64(1.0 / 6.0).unwrap();
150 let third = N::from_f64(1.0 / 3.0).unwrap();
151 let half_real = N::from_f64(0.5).unwrap().real();
152 let one_and_a_half_real = N::from_f64(1.5).unwrap().real();
153 let four = N::from_i32(4).unwrap();
154
155 let mut area = N::zero();
156 let mut i = 1;
157 let mut tol_i = vec![N::from_i32(10).unwrap().real() * tol];
158 let mut left_i = vec![left];
159 let mut step_i = vec![(right - left) * half_real];
160 let mut f_ai = vec![f(left)];
161 let mut f_ci = vec![f(left + step_i[0])];
162 let mut f_bi = vec![f(right)];
163 let mut sum_i = vec![N::from_real(step_i[0]) * (f_ai[0] + four * f_ci[0] + f_bi[0]) * third];
164 let mut l_i = vec![1];
165
166 while i > 0 {
167 let f_d = f(left_i[i - 1] + half_real * step_i[i - 1]);
168 let f_e = f(left_i[i - 1] + one_and_a_half_real * step_i[i - 1]);
169 let s1 = N::from_real(step_i[i - 1]) * (f_ai[i - 1] + four * f_d + f_ci[i - 1]) * sixth;
170 let s2 = N::from_real(step_i[i - 1]) * (f_ci[i - 1] + four * f_e + f_bi[i - 1]) * sixth;
171 let v_1 = left_i[i - 1];
172 let v_2 = f_ai[i - 1];
173 let v_3 = f_ci[i - 1];
174 let v_4 = f_bi[i - 1];
175 let v_5 = step_i[i - 1];
176 let v_6 = tol_i[i - 1];
177 let v_7 = sum_i[i - 1];
178 let v_8 = l_i[i - 1];
179
180 i -= 1;
181
182 if (s1 + s2 - v_7).abs() < v_6 {
183 area += s1 + s2;
184 } else {
185 if v_8 >= n_max {
186 return Err("integrate: maximum iterations exceeded".to_owned());
187 }
188
189 i += 1;
190 if i > left_i.len() {
191 left_i.push(v_1 + v_5);
192 f_ai.push(v_3);
193 f_ci.push(f_e);
194 f_bi.push(v_4);
195 step_i.push(half_real * v_5);
196 tol_i.push(half_real * v_6);
197 sum_i.push(s2);
198 l_i.push(v_8 + 1);
199 } else {
200 left_i[i - 1] = v_1 + v_5;
201 f_ai[i - 1] = v_3;
202 f_ci[i - 1] = f_e;
203 f_bi[i - 1] = v_4;
204 step_i[i - 1] = half_real * v_5;
205 tol_i[i - 1] = half_real * v_6;
206 sum_i[i - 1] = s2;
207 l_i[i - 1] = v_8 + 1;
208 }
209
210 i += 1;
211 if i > left_i.len() {
212 left_i.push(v_1);
213 f_ai.push(v_2);
214 f_ci.push(f_d);
215 f_bi.push(v_3);
216 step_i.push(step_i[i - 2]);
217 tol_i.push(tol_i[i - 2]);
218 sum_i.push(s1);
219 l_i.push(l_i[i - 2]);
220 } else {
221 left_i[i - 1] = v_1;
222 f_ai[i - 1] = v_2;
223 f_ci[i - 1] = f_d;
224 f_bi[i - 1] = v_3;
225 step_i[i - 1] = step_i[i - 2];
226 tol_i[i - 1] = tol_i[i - 2];
227 sum_i[i - 1] = sum_i[i - 2];
228 l_i[i - 1] = l_i[i - 2];
229 }
230 }
231 }
232
233 Ok(area)
234}
235
236pub fn integrate_fixed<N: ComplexField + FromPrimitive + Copy, F: FnMut(N::RealField) -> N>(
241 left: N::RealField,
242 right: N::RealField,
243 mut f: F,
244 n: usize,
245) -> Result<N, String>
246where
247 <N as ComplexField>::RealField: FromPrimitive + Copy,
248{
249 if left >= right {
250 return Err("integrate_fixed: left must be less than right".to_owned());
251 }
252
253 let half = N::from_f64(0.5).unwrap();
254 let half_real = N::from_f64(0.5).unwrap().real();
255 let four = N::from_i32(4).unwrap();
256
257 let mut h = right - left;
258
259 let mut prev_rows = vec![N::zero(); n];
260 prev_rows[0] = N::from_real(h) * half * (f(left) + f(right));
261 let mut next = vec![N::zero(); n];
262
263 for i in 2..=n {
264 let mut acc = N::zero();
265 for k in 1..=(1 << (i - 2)) {
266 acc += f(left + N::from_f64(k as f64 - 0.5).unwrap().real() * h);
267 }
268 acc *= N::from_real(h);
269 acc += prev_rows[0];
270 acc *= half;
271 next[0] = acc;
272 for j in 2..=i {
273 next[j - 1] = next[j - 2]
274 + (next[j - 2] - prev_rows[j - 2]) / (four.powi(j as i32 - 1) - N::one());
275 }
276
277 h *= half_real;
278 prev_rows[..i].clone_from_slice(&next[..i]);
279 }
280
281 Ok(prev_rows[n - 1])
282}