1use std::ops::AddAssign;
9
10use anyhow::Error;
11use num::{Float, FromPrimitive};
12
13use crate::pert::PerturbationVectors;
14use crate::utils::mod_and_calc;
15
16use super::OpFn;
17
18pub fn forward_jacobian_vec<F>(x: &Vec<F>, fs: OpFn<'_, F>) -> Result<Vec<Vec<F>>, Error>
19where
20 F: Float + FromPrimitive,
21{
22 let fx = (fs)(x)?;
23 let mut xt = x.clone();
24 let eps_sqrt = F::epsilon().sqrt();
25 let mut out: Vec<Vec<F>> = vec![vec![F::from_f64(0.0).unwrap(); x.len()]; fx.len()];
26 for j in 0..x.len() {
27 let fx1 = mod_and_calc(&mut xt, fs, j, eps_sqrt)?;
28 for i in 0..fx.len() {
29 out[i][j] = (fx1[i] - fx[i]) / eps_sqrt;
30 }
31 }
32 Ok(out)
33}
34
35pub fn central_jacobian_vec<F>(x: &[F], fs: OpFn<'_, F>) -> Result<Vec<Vec<F>>, Error>
36where
37 F: Float + FromPrimitive,
38{
39 let mut xt = x.to_owned();
40 let eps_cbrt = F::epsilon().cbrt();
41
42 let comp = |(a, b): (&F, &F)| (*a - *b) / (F::from_f64(2.0).unwrap() * eps_cbrt);
43
44 let fx1 = mod_and_calc(&mut xt, fs, 0, eps_cbrt)?;
47 let fx2 = mod_and_calc(&mut xt, fs, 0, -eps_cbrt)?;
48 let t0 = fx1.iter().zip(fx2.iter()).map(comp).collect::<Vec<F>>();
49
50 let mut out: Vec<Vec<F>> = vec![vec![F::from_f64(0.0).unwrap(); x.len()]; fx1.len()];
52
53 for i in 0..t0.len() {
55 out[i][0] = t0[i];
56 }
57
58 for j in 1..x.len() {
60 let fx1 = mod_and_calc(&mut xt, fs, j, eps_cbrt)?;
61 let fx2 = mod_and_calc(&mut xt, fs, j, -eps_cbrt)?;
62 for i in 0..fx1.len() {
63 out[i][j] = comp((&fx1[i], &fx2[i]));
64 }
65 }
66 Ok(out)
67}
68
69pub fn forward_jacobian_vec_prod_vec<F>(
70 x: &Vec<F>,
71 fs: OpFn<'_, F>,
72 p: &[F],
73) -> Result<Vec<F>, Error>
74where
75 F: Float,
76{
77 let fx = (fs)(x)?;
78 let eps_sqrt = F::epsilon().sqrt();
79 let x1 = x
80 .iter()
81 .zip(p.iter())
82 .map(|(&xi, &pi)| xi + eps_sqrt * pi)
83 .collect();
84 let fx1 = (fs)(&x1)?;
85 fx1.iter()
86 .zip(fx.iter())
87 .map(|(&a, &b)| Ok((a - b) / eps_sqrt))
88 .collect::<Result<Vec<F>, Error>>()
89}
90
91pub fn central_jacobian_vec_prod_vec<F>(x: &[F], fs: OpFn<'_, F>, p: &[F]) -> Result<Vec<F>, Error>
92where
93 F: Float + FromPrimitive,
94{
95 let eps_cbrt = F::epsilon().cbrt();
96 let x1 = x
98 .iter()
99 .zip(p.iter())
100 .map(|(&xi, &pi)| xi + eps_cbrt * pi)
101 .collect();
102 let x2 = x
103 .iter()
104 .zip(p.iter())
105 .map(|(&xi, &pi)| xi - eps_cbrt * pi)
106 .collect();
107 let fx1 = (fs)(&x1)?;
108 let fx2 = (fs)(&x2)?;
109 fx1.iter()
110 .zip(fx2.iter())
111 .map(|(&a, &b)| Ok((a - b) / (F::from_f64(2.0).unwrap() * eps_cbrt)))
112 .collect::<Result<Vec<F>, Error>>()
113}
114
115pub fn forward_jacobian_pert_vec<F>(
116 x: &Vec<F>,
117 fs: OpFn<'_, F>,
118 pert: &PerturbationVectors,
119) -> Result<Vec<Vec<F>>, Error>
120where
121 F: Float + FromPrimitive + AddAssign,
122{
123 let fx = (fs)(x)?;
124 let eps_sqrt = F::epsilon().sqrt();
125 let mut xt = x.clone();
126 let mut out = vec![vec![F::from_f64(0.0).unwrap(); x.len()]; fx.len()];
127 for pert_item in pert.iter() {
128 for i in pert_item.x_idx.iter() {
129 xt[*i] += eps_sqrt;
130 }
131
132 let fx1 = (fs)(&xt)?;
133
134 for i in pert_item.x_idx.iter() {
135 xt[*i] = x[*i];
136 }
137
138 for (k, x_idx) in pert_item.x_idx.iter().enumerate() {
139 for i in pert_item.r_idx[k].iter() {
140 out[*i][*x_idx] = (fx1[*i] - fx[*i]) / eps_sqrt;
141 }
142 }
143 }
144 Ok(out)
145}
146
147pub fn central_jacobian_pert_vec<F>(
148 x: &[F],
149 fs: OpFn<'_, F>,
150 pert: &PerturbationVectors,
151) -> Result<Vec<Vec<F>>, Error>
152where
153 F: Float + FromPrimitive + AddAssign,
154{
155 let mut out = vec![];
156 let eps_cbrt = F::epsilon().cbrt();
157 let mut xt = x.to_owned();
158 for (i, pert_item) in pert.iter().enumerate() {
159 for j in pert_item.x_idx.iter() {
160 xt[*j] += eps_cbrt;
161 }
162
163 let fx1 = (fs)(&xt)?;
164
165 for j in pert_item.x_idx.iter() {
166 xt[*j] = x[*j] - eps_cbrt;
167 }
168
169 let fx2 = (fs)(&xt)?;
170
171 for j in pert_item.x_idx.iter() {
172 xt[*j] = x[*j];
173 }
174
175 if i == 0 {
177 out = vec![vec![F::from_f64(0.0).unwrap(); x.len()]; fx1.len()];
178 }
179
180 for (k, x_idx) in pert_item.x_idx.iter().enumerate() {
181 for j in pert_item.r_idx[k].iter() {
182 out[*j][*x_idx] = (fx1[*j] - fx2[*j]) / (F::from_f64(2.0).unwrap() * eps_cbrt);
183 }
184 }
185 }
186 Ok(out)
187}
188
189#[cfg(test)]
190mod tests {
191 use crate::PerturbationVector;
192
193 use super::*;
194
195 const COMP_ACC: f64 = 1e-6;
196
197 fn f(x: &Vec<f64>) -> Result<Vec<f64>, Error> {
198 Ok(vec![
199 2.0 * (x[1].powi(3) - x[0].powi(2)),
200 3.0 * (x[1].powi(3) - x[0].powi(2)) + 2.0 * (x[2].powi(3) - x[1].powi(2)),
201 3.0 * (x[2].powi(3) - x[1].powi(2)) + 2.0 * (x[3].powi(3) - x[2].powi(2)),
202 3.0 * (x[3].powi(3) - x[2].powi(2)) + 2.0 * (x[4].powi(3) - x[3].powi(2)),
203 3.0 * (x[4].powi(3) - x[3].powi(2)) + 2.0 * (x[5].powi(3) - x[4].powi(2)),
204 3.0 * (x[5].powi(3) - x[4].powi(2)),
205 ])
206 }
207
208 fn res1() -> Vec<Vec<f64>> {
209 vec![
210 vec![-4.0, 6.0, 0.0, 0.0, 0.0, 0.0],
211 vec![-6.0, 5.0, 6.0, 0.0, 0.0, 0.0],
212 vec![0.0, -6.0, 5.0, 6.0, 0.0, 0.0],
213 vec![0.0, 0.0, -6.0, 5.0, 6.0, 0.0],
214 vec![0.0, 0.0, 0.0, -6.0, 5.0, 6.0],
215 vec![0.0, 0.0, 0.0, 0.0, -6.0, 9.0],
216 ]
217 }
218
219 fn res2() -> Vec<f64> {
220 vec![8.0, 22.0, 27.0, 32.0, 37.0, 24.0]
221 }
222
223 fn x() -> Vec<f64> {
224 vec![1.0f64, 1.0, 1.0, 1.0, 1.0, 1.0]
225 }
226
227 fn p() -> Vec<f64> {
228 vec![1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0]
229 }
230
231 fn pert() -> PerturbationVectors {
232 vec![
233 PerturbationVector::new()
234 .add(0, vec![0, 1])
235 .add(3, vec![2, 3, 4]),
236 PerturbationVector::new()
237 .add(1, vec![0, 1, 2])
238 .add(4, vec![3, 4, 5]),
239 PerturbationVector::new()
240 .add(2, vec![1, 2, 3])
241 .add(5, vec![4, 5]),
242 ]
243 }
244
245 #[test]
246 fn test_forward_jacobian_vec_f64() {
247 let jacobian = forward_jacobian_vec(&x(), &f).unwrap();
248 let res = res1();
249 for i in 0..6 {
251 for j in 0..6 {
252 assert!((res[i][j] - jacobian[i][j]).abs() < COMP_ACC)
253 }
254 }
255 }
256
257 #[test]
258 fn test_central_jacobian_vec_f64() {
259 let jacobian = central_jacobian_vec(&x(), &f).unwrap();
260 let res = res1();
261 for i in 0..6 {
263 for j in 0..6 {
264 assert!((res[i][j] - jacobian[i][j]).abs() < COMP_ACC);
265 }
266 }
267 }
268
269 #[test]
270 fn test_forward_jacobian_vec_prod_vec_f64() {
271 let jacobian = forward_jacobian_vec_prod_vec(&x(), &f, &p()).unwrap();
272 let res = res2();
273 for i in 0..6 {
276 assert!((res[i] - jacobian[i]).abs() < 11.0 * COMP_ACC)
277 }
278 }
279
280 #[test]
281 fn test_central_jacobian_vec_prod_vec_f64() {
282 let jacobian = central_jacobian_vec_prod_vec(&x(), &f, &p()).unwrap();
283 let res = res2();
284 for i in 0..6 {
286 assert!((res[i] - jacobian[i]).abs() < COMP_ACC)
287 }
288 }
289
290 #[test]
291 fn test_forward_jacobian_pert_vec_f64() {
292 let jacobian = forward_jacobian_pert_vec(&x(), &f, &pert()).unwrap();
293 let res = res1();
294 for i in 0..6 {
297 for j in 0..6 {
298 assert!((res[i][j] - jacobian[i][j]).abs() < COMP_ACC)
299 }
300 }
301 }
302
303 #[test]
304 fn test_central_jacobian_pert_vec_f64() {
305 let jacobian = central_jacobian_pert_vec(&x(), &f, &pert()).unwrap();
306 let res = res1();
307 for i in 0..6 {
310 for j in 0..6 {
311 assert!((res[i][j] - jacobian[i][j]).abs() < COMP_ACC)
312 }
313 }
314 }
315}