use crate::array::Array;
use crate::error::{NumRs2Error, Result};
use num_traits::Float;
use crate::autodiff::{Dual, Tape, Var};
pub fn forward_jacobian<F, T>(f: F, x: &[T]) -> Result<Array<T>>
where
F: Fn(&[Dual<T>]) -> Vec<Dual<T>>,
T: Float,
{
let n = x.len();
if n == 0 {
return Err(NumRs2Error::InvalidInput(
"Input vector must be non-empty".to_string(),
));
}
let probe_input: Vec<Dual<T>> = x.iter().map(|&v| Dual::variable(v)).collect();
let probe_output = f(&probe_input);
let m = probe_output.len();
if m == 0 {
return Err(NumRs2Error::InvalidInput(
"Output vector must be non-empty".to_string(),
));
}
let mut jac_data = vec![T::zero(); m * n];
for j in 0..n {
let dual_input: Vec<Dual<T>> = (0..n)
.map(|k| {
let deriv = if k == j { T::one() } else { T::zero() };
Dual::new(x[k], deriv)
})
.collect();
let output = f(&dual_input);
for i in 0..m {
jac_data[i * n + j] = output[i].deriv();
}
}
Ok(Array::from_vec(jac_data).reshape(&[m, n]))
}
pub fn reverse_jacobian<F, T>(f: F, x: &[T]) -> Result<Array<T>>
where
F: Fn(&mut Tape<T>, &[Var]) -> Vec<Var>,
T: Float,
{
let n = x.len();
if n == 0 {
return Err(NumRs2Error::InvalidInput(
"Input vector must be non-empty".to_string(),
));
}
let mut tape = Tape::new();
let vars: Vec<Var> = x.iter().map(|&v| tape.var(v)).collect();
let outputs = f(&mut tape, &vars);
let m = outputs.len();
if m == 0 {
return Err(NumRs2Error::InvalidInput(
"Output vector must be non-empty".to_string(),
));
}
let mut jac_data = vec![T::zero(); m * n];
for i in 0..m {
tape.zero_grad();
tape.backward(outputs[i]);
for j in 0..n {
jac_data[i * n + j] = tape.grad(vars[j]);
}
}
Ok(Array::from_vec(jac_data).reshape(&[m, n]))
}
pub fn jacobian_auto<F, T>(f: F, x: &[T]) -> Result<Array<T>>
where
F: Fn(&[Dual<T>]) -> Vec<Dual<T>>,
T: Float,
{
forward_jacobian(f, x)
}