use num_traits::{One, Zero, real::Real};
use diffable::traits::{
Euclidean, Tensor,
calculus::{Jet, JetRegion, JetVector},
ι, 𝐑𝐞𝐚𝐥,
};
pub fn jacobian_of<V, F, G>(f: F, point: &V) -> Vec<V::F>
where
V: Euclidean,
V::F: Real + ι<C: JetRegion<𝐑𝐞𝐚𝐥::𝒞>>,
F: Fn(JetVector<𝐑𝐞𝐚𝐥::𝒞, V, 1, V::F>) -> G,
G: Tensor<F = Jet<𝐑𝐞𝐚𝐥::𝒞, V::F, 1>>,
{
let mut out = vec![V::F::zero(); G::N * V::N];
for i in 0..V::N {
let input = JetVector::<𝐑𝐞𝐚𝐥::𝒞, V, 1, V::F>::from_fn(|c| {
Jet::new(point[c], [if c == i { V::F::one() } else { V::F::zero() }])
});
let g = f(input);
for o in 0..G::N {
out[o * V::N + i] = g[o][1];
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use diffable::coords::Coords;
use diffable::traits::{Dual, Right, Sinister, Tensor, calculus::TensorProduct};
fn cube<V: Euclidean>(x: V) -> V {
x.map(|x| x.powi(3))
}
#[test]
fn derivative_of_cube() {
let j = jacobian_of(cube, &Coords([2.0]));
assert!((j[0] - 12.0).abs() < 1e-12); }
#[test]
fn derivative_of_tensor_values() {
type G<V> = TensorProduct<Sinister<Dual<V>>, Dual<V>>;
fn g<V: Euclidean + Tensor<Hand = Right>>(x: V) -> G<V> {
TensorProduct::from_fn_ij(|i, j| {
if i == j {
if i == 0 { V::F::one() } else { x[0] * x[0] }
} else {
V::F::zero()
}
})
}
let j = jacobian_of(g, &Coords([3.0, 0.0]));
assert!((j[6] - 6.0).abs() < 1e-12);
}
}