use crate::areal::AReal;
use crate::freal::FReal;
use crate::traits::Scalar;
use crate::tape::{Tape, TapeStorage};
pub fn compute_jacobian_rev<T, F>(inputs: &[T], func: F) -> Vec<Vec<T>>
where
T: TapeStorage,
F: Fn(&[AReal<T>]) -> Vec<AReal<T>>,
{
let mut tape = Tape::<T>::new(true);
tape.activate();
let mut ad_inputs: Vec<AReal<T>> = inputs.iter().map(|&v| AReal::new(v)).collect();
AReal::register_input(&mut ad_inputs, &mut tape);
let mut ad_outputs = func(&ad_inputs);
AReal::register_output(&mut ad_outputs, &mut tape);
let num_inputs = ad_inputs.len();
let num_outputs = ad_outputs.len();
let mut jacobian = vec![vec![T::zero(); num_inputs]; num_outputs];
for i in 0..num_outputs {
tape.clear_derivatives();
ad_outputs[i].set_adjoint(&mut tape, T::one());
tape.compute_adjoints();
for j in 0..num_inputs {
jacobian[i][j] = ad_inputs[j].adjoint(&tape);
}
}
Tape::<T>::deactivate_all();
jacobian
}
pub fn compute_jacobian_fwd<T, F>(inputs: &[T], func: F) -> Vec<Vec<T>>
where
T: Scalar,
F: Fn(&[FReal<T>]) -> Vec<FReal<T>>,
{
let zero_inputs: Vec<FReal<T>> = inputs
.iter()
.map(|&v| FReal::constant(v))
.collect();
let num_outputs = func(&zero_inputs).len();
let num_inputs = inputs.len();
let mut jacobian = vec![vec![T::zero(); num_inputs]; num_outputs];
for j in 0..num_inputs {
let mut fwd_inputs: Vec<FReal<T>> = inputs
.iter()
.map(|&v| FReal::constant(v))
.collect();
fwd_inputs[j].set_derivative(T::one());
let fwd_outputs = func(&fwd_inputs);
for i in 0..num_outputs {
jacobian[i][j] = fwd_outputs[i].derivative();
}
}
jacobian
}