1use fenris_traits::Real;
2use nalgebra::{DMatrix, DMatrixViewMut, DVector, DVectorView, DVectorViewMut, Dim, DimName, Dyn, Scalar, Vector, U1};
3
4use nalgebra::base::storage::{Storage, StorageMut};
5use numeric_literals::replace_float_literals;
6use std::error::Error;
7
8pub trait VectorFunction<T>
9where
10 T: Scalar,
11{
12 fn dimension(&self) -> usize;
13 fn eval_into(&mut self, f: &mut DVectorViewMut<T>, x: &DVectorView<T>);
14}
15
16impl<T, X> VectorFunction<T> for &mut X
17where
18 T: Scalar,
19 X: VectorFunction<T>,
20{
21 fn dimension(&self) -> usize {
22 X::dimension(self)
23 }
24
25 fn eval_into(&mut self, f: &mut DVectorViewMut<T>, x: &DVectorView<T>) {
26 X::eval_into(self, f, x)
27 }
28}
29
30pub trait DifferentiableVectorFunction<T>: VectorFunction<T>
31where
32 T: Scalar,
33{
34 fn solve_jacobian_system(
35 &mut self,
36 sol: &mut DVectorViewMut<T>,
37 x: &DVectorView<T>,
38 rhs: &DVectorView<T>,
39 ) -> Result<(), Box<dyn Error>>;
40}
41
42impl<T, X> DifferentiableVectorFunction<T> for &mut X
43where
44 T: Scalar,
45 X: DifferentiableVectorFunction<T>,
46{
47 fn solve_jacobian_system(
48 &mut self,
49 sol: &mut DVectorViewMut<T>,
50 x: &DVectorView<T>,
51 rhs: &DVectorView<T>,
52 ) -> Result<(), Box<dyn Error>> {
53 X::solve_jacobian_system(self, sol, x, rhs)
54 }
55}
56
57#[derive(Debug, Clone)]
58pub struct VectorFunctionBuilder {
59 dimension: usize,
60}
61
62#[derive(Debug, Clone)]
63pub struct ConcreteVectorFunction<F, J> {
64 dimension: usize,
65 function: F,
66 jacobian_solver: J,
67}
68
69impl VectorFunctionBuilder {
70 pub fn with_dimension(dimension: usize) -> Self {
71 Self { dimension }
72 }
73
74 pub fn with_function<F, T>(self, function: F) -> ConcreteVectorFunction<F, ()>
75 where
76 T: Scalar,
77 F: FnMut(&mut DVectorViewMut<T>, &DVectorView<T>),
78 {
79 ConcreteVectorFunction {
80 dimension: self.dimension,
81 function,
82 jacobian_solver: (),
83 }
84 }
85}
86
87impl<F> ConcreteVectorFunction<F, ()> {
88 pub fn with_jacobian_solver<J, T>(self, jacobian_solver: J) -> ConcreteVectorFunction<F, J>
89 where
90 T: Scalar,
91 J: FnMut(&mut DVectorViewMut<T>, &DVectorView<T>, &DVectorView<T>) -> Result<(), Box<dyn Error>>,
92 {
93 ConcreteVectorFunction {
94 dimension: self.dimension,
95 function: self.function,
96 jacobian_solver,
97 }
98 }
99}
100
101impl<F, J, T> VectorFunction<T> for ConcreteVectorFunction<F, J>
102where
103 T: Scalar,
104 F: FnMut(&mut DVectorViewMut<T>, &DVectorView<T>),
105{
106 fn dimension(&self) -> usize {
107 self.dimension
108 }
109
110 fn eval_into(&mut self, f: &mut DVectorViewMut<T>, x: &DVectorView<T>) {
111 let func = &mut self.function;
112 func(f, x)
113 }
114}
115
116impl<F, J, T> DifferentiableVectorFunction<T> for ConcreteVectorFunction<F, J>
117where
118 T: Scalar,
119 F: FnMut(&mut DVectorViewMut<T>, &DVectorView<T>),
120 J: FnMut(&mut DVectorViewMut<T>, &DVectorView<T>, &DVectorView<T>) -> Result<(), Box<dyn Error>>,
121{
122 fn solve_jacobian_system(
123 &mut self,
124 sol: &mut DVectorViewMut<T>,
125 x: &DVectorView<T>,
126 rhs: &DVectorView<T>,
127 ) -> Result<(), Box<dyn Error>> {
128 let j = &mut self.jacobian_solver;
129 j(sol, x, rhs)
130 }
131}
132
133fn as_vector_slice<T, R, S>(vector: &Vector<T, R, S>) -> DVectorView<T>
135where
136 T: Scalar,
137 S: Storage<T, R, U1, RStride = U1, CStride = Dyn>,
138 R: Dim,
139{
140 vector.generic_view((0, 0), (Dyn(vector.nrows()), U1::name()))
141}
142
143fn as_vector_slice_mut<T, R, S>(vector: &mut Vector<T, R, S>) -> DVectorViewMut<T>
145where
146 T: Scalar,
147 S: StorageMut<T, R, U1, RStride = U1, CStride = Dyn>,
148 R: Dim,
149{
150 vector.generic_view_mut((0, 0), (Dyn(vector.nrows()), U1::name()))
151}
152
153#[replace_float_literals(T::from_f64(literal).expect("Literal must fit in T"))]
156pub fn approximate_jacobian<T>(mut f: impl VectorFunction<T>, x: &DVector<T>, h: &T) -> DMatrix<T>
157where
158 T: Real,
159{
160 let out_dim = f.dimension();
161 let in_dim = x.len();
162
163 let mut result = DMatrix::zeros(out_dim, in_dim);
164
165 let mut x_plus = x.clone();
171 let mut x_minus = x.clone();
172
173 let mut f_plus = DVector::zeros(out_dim);
176 let mut f_minus = DVector::zeros(out_dim);
177
178 for j in 0..in_dim {
180 x_plus.copy_from(x);
182 x_plus[j] += *h;
183 x_minus.copy_from(x);
184 x_minus[j] -= *h;
185
186 f.eval_into(&mut as_vector_slice_mut(&mut f_plus), &as_vector_slice(&x_plus));
187 f.eval_into(&mut as_vector_slice_mut(&mut f_minus), &as_vector_slice(&x_minus));
188
189 let mut column_j = result.column_mut(j);
191 column_j += &f_plus;
192 column_j -= &f_minus;
193 column_j /= 2.0 * *h;
194 }
195
196 result
197}
198
199pub fn approximate_gradient_fd<'a, T>(
206 f: impl FnMut(DVectorView<T>) -> T,
207 x: impl Into<DVectorViewMut<'a, T>>,
208 h: T,
209) -> DVector<T>
210where
211 T: Real,
212{
213 let x = x.into();
214 let mut df = DVector::zeros(x.len());
215 approximate_gradient_fd_into_(DVectorViewMut::from(&mut df), f, x, h);
216 df
217}
218
219pub fn approximate_gradient_fd_into<'a, T>(
227 mut df: DVectorViewMut<T>,
228 f: impl FnMut(DVectorView<T>) -> T,
229 x: impl Into<DVectorViewMut<'a, T>>,
230 h: T,
231) where
232 T: Real,
233{
234 approximate_gradient_fd_into_(DVectorViewMut::from(&mut df), f, x.into(), h);
235}
236
237#[replace_float_literals(T::from_f64(literal).unwrap())]
238fn approximate_gradient_fd_into_<T>(
239 mut df: DVectorViewMut<T>,
240 mut f: impl FnMut(DVectorView<T>) -> T,
241 mut x: DVectorViewMut<T>,
242 h: T,
243) where
244 T: Real,
245{
246 let n = x.len();
247 for i in 0..n {
248 let x_i = x[i];
249 x[i] = x_i + h;
250 let f_plus = f(DVectorView::from(&x));
251 x[i] = x_i - h;
252 let f_minus = f(DVectorView::from(&x));
253 let df_i = (f_plus - f_minus) / (2.0 * h);
254 df[i] = df_i;
255 x[i] = x_i;
256 }
257}
258
259pub fn approximate_jacobian_fd<'a, T>(
267 m: usize,
268 f: impl FnMut(DVectorView<T>, DVectorViewMut<T>),
269 x: impl Into<DVectorViewMut<'a, T>>,
270 h: T,
271) -> DMatrix<T>
272where
273 T: Real,
274{
275 let x = x.into();
276 let n = x.len();
277 let mut jacobian = DMatrix::zeros(m, n);
278 approximate_jacobian_fd_into_(DMatrixViewMut::from(&mut jacobian), f, x, h);
279 jacobian
280}
281
282pub fn approximate_jacobian_fd_into<'a, T>(
287 jacobian: impl Into<DMatrixViewMut<'a, T>>,
288 f: impl FnMut(DVectorView<T>, DVectorViewMut<T>),
289 x: impl Into<DVectorViewMut<'a, T>>,
290 h: T,
291) where
292 T: Real,
293{
294 approximate_jacobian_fd_into_(jacobian.into(), f, x.into(), h);
295}
296
297#[replace_float_literals(T::from_f64(literal).unwrap())]
298fn approximate_jacobian_fd_into_<T>(
299 mut j: DMatrixViewMut<T>,
300 mut f: impl FnMut(DVectorView<T>, DVectorViewMut<T>),
301 mut x: DVectorViewMut<T>,
302 h: T,
303) where
304 T: Real,
305{
306 let m = j.nrows();
307 let n = x.len();
308 assert_eq!(n, j.ncols());
309
310 let mut f_plus = DVector::zeros(m);
312 let mut f_minus = DVector::zeros(m);
313
314 for i in 0..n {
317 let xi = x[i];
319 x[i] = xi + h;
320 f(DVectorView::from(&x), DVectorViewMut::from(&mut f_plus));
321 x[i] = xi - h;
322 f(DVectorView::from(&x), DVectorViewMut::from(&mut f_minus));
323 x[i] = xi;
324
325 let mut df_dxi = j.column_mut(i);
326 df_dxi.copy_from(&f_plus);
327 df_dxi -= &f_minus;
328 df_dxi /= 2.0 * h;
329 }
330}