use na::{allocator::Allocator, DefaultAllocator, DimName, MatrixMN, Real, VectorN};
use {Dual, Num, One, Scalar};
#[inline]
pub fn differentiate<T: Scalar + One, F>(x: T, f: F) -> T
where
F: Fn(Dual<T>) -> Dual<T>,
{
f(Dual::new(x, T::one())).dual()
}
pub fn partials_t<T: Real + Num, M: DimName, N: DimName, F>(t: T, x: VectorN<T, M>, f: F) -> (VectorN<T, N>, MatrixMN<T, N, M>)
where
F: Fn(T, &MatrixMN<Dual<T>, M, M>) -> MatrixMN<Dual<T>, N, M>,
DefaultAllocator:
Allocator<Dual<T>, M> + Allocator<Dual<T>, N, M> + Allocator<Dual<T>, M, M> + Allocator<T, N> + Allocator<T, M> + Allocator<T, N, M>,
{
let mut hyperdual_space = MatrixMN::<Dual<T>, M, M>::zeros();
for i in 0..M::dim() {
let mut v_i = VectorN::<Dual<T>, M>::zeros();
for j in 0..M::dim() {
v_i[(j, 0)] = Dual::new(x[(j, 0)], if i == j { T::one() } else { T::zero() });
}
hyperdual_space.set_column(i, &v_i);
}
let state_n_grad = f(t, &hyperdual_space);
let mut state = VectorN::<T, N>::zeros();
let mut grad = MatrixMN::<T, N, M>::zeros();
for i in 0..N::dim() {
for j in 0..M::dim() {
if j == 0 {
state[(i, 0)] = state_n_grad[(i, 0)].real();
}
grad[(i, j)] = state_n_grad[(i, j)].dual();
}
}
(state, grad)
}
#[inline]
pub fn partials<T: Real + Num, M: DimName, N: DimName, F>(x: VectorN<T, M>, f: F) -> (VectorN<T, N>, MatrixMN<T, N, M>)
where
F: Fn(&MatrixMN<Dual<T>, M, M>) -> MatrixMN<Dual<T>, N, M>,
DefaultAllocator:
Allocator<Dual<T>, M> + Allocator<Dual<T>, N, M> + Allocator<Dual<T>, M, M> + Allocator<T, N> + Allocator<T, M> + Allocator<T, N, M>,
{
partials_t(T::zero(), x, |_, x| f(x))
}