use ndarray::{Array1, Array2};
use crate::bytecode_tape::{BtapeThreadLocal, BytecodeTape};
use crate::float::Float;
use crate::BReverse;
#[inline]
fn to_vec<F: Copy>(x: &Array1<F>) -> Vec<F> {
x.iter().copied().collect()
}
pub fn grad_ndarray<F: Float + BtapeThreadLocal>(
f: impl FnOnce(&[BReverse<F>]) -> BReverse<F>,
x: &Array1<F>,
) -> Array1<F> {
let xs = to_vec(x);
let (mut tape, _) = crate::api::record(f, &xs);
let g = tape.gradient(&xs);
Array1::from_vec(g)
}
pub fn grad_ndarray_val<F: Float + BtapeThreadLocal>(
f: impl FnOnce(&[BReverse<F>]) -> BReverse<F>,
x: &Array1<F>,
) -> (F, Array1<F>) {
let xs = to_vec(x);
let (mut tape, val) = crate::api::record(f, &xs);
let g = tape.gradient(&xs);
(val, Array1::from_vec(g))
}
pub fn hessian_ndarray<F: Float + BtapeThreadLocal>(
f: impl FnOnce(&[BReverse<F>]) -> BReverse<F>,
x: &Array1<F>,
) -> (F, Array1<F>, Array2<F>) {
let xs = to_vec(x);
let (tape, _) = crate::api::record(f, &xs);
let (val, grad, hess) = tape.hessian(&xs);
let n = xs.len();
let hess_flat: Vec<F> = hess.into_iter().flat_map(|row| row.into_iter()).collect();
(
val,
Array1::from_vec(grad),
Array2::from_shape_vec((n, n), hess_flat).unwrap(),
)
}
pub fn jacobian_ndarray<F: Float + BtapeThreadLocal>(
f: impl FnOnce(&[BReverse<F>]) -> Vec<BReverse<F>>,
x: &Array1<F>,
) -> Array2<F> {
let xs = to_vec(x);
let (mut tape, _) = crate::api::record_multi(f, &xs);
let jac = tape.jacobian(&xs);
let m = jac.len();
let n = if m > 0 { jac[0].len() } else { xs.len() };
let flat: Vec<F> = jac.into_iter().flat_map(|row| row.into_iter()).collect();
Array2::from_shape_vec((m, n), flat).unwrap()
}
pub fn tape_gradient_ndarray<F: Float>(tape: &mut BytecodeTape<F>, x: &Array1<F>) -> Array1<F> {
let xs = to_vec(x);
let g = tape.gradient(&xs);
Array1::from_vec(g)
}
#[must_use]
pub fn tape_hessian_ndarray<F: Float>(
tape: &BytecodeTape<F>,
x: &Array1<F>,
) -> (F, Array1<F>, Array2<F>) {
let xs = to_vec(x);
let (val, grad, hess) = tape.hessian(&xs);
let n = xs.len();
let hess_flat: Vec<F> = hess.into_iter().flat_map(|row| row.into_iter()).collect();
(
val,
Array1::from_vec(grad),
Array2::from_shape_vec((n, n), hess_flat).unwrap(),
)
}
pub fn hvp_ndarray<F: Float + BtapeThreadLocal>(
f: impl FnOnce(&[BReverse<F>]) -> BReverse<F>,
x: &Array1<F>,
v: &Array1<F>,
) -> (Array1<F>, Array1<F>) {
let xs = to_vec(x);
let vs = to_vec(v);
let (grad, hvp) = crate::api::hvp(f, &xs, &vs);
(Array1::from_vec(grad), Array1::from_vec(hvp))
}
#[must_use]
pub fn tape_hvp_ndarray<F: Float>(
tape: &BytecodeTape<F>,
x: &Array1<F>,
v: &Array1<F>,
) -> (Array1<F>, Array1<F>) {
let xs = to_vec(x);
let vs = to_vec(v);
let (grad, hvp) = tape.hvp(&xs, &vs);
(Array1::from_vec(grad), Array1::from_vec(hvp))
}
pub fn sparse_hessian_ndarray<F: Float + BtapeThreadLocal>(
f: impl FnOnce(&[BReverse<F>]) -> BReverse<F>,
x: &Array1<F>,
) -> (F, Array1<F>, crate::sparse::SparsityPattern, Array1<F>) {
let xs = to_vec(x);
let (val, grad, pattern, values) = crate::api::sparse_hessian(f, &xs);
(
val,
Array1::from_vec(grad),
pattern,
Array1::from_vec(values),
)
}
#[must_use]
pub fn tape_sparse_hessian_ndarray<F: Float>(
tape: &BytecodeTape<F>,
x: &Array1<F>,
) -> (F, Array1<F>, crate::sparse::SparsityPattern, Array1<F>) {
let xs = to_vec(x);
let (val, grad, pattern, values) = tape.sparse_hessian(&xs);
(
val,
Array1::from_vec(grad),
pattern,
Array1::from_vec(values),
)
}
pub fn sparse_jacobian_ndarray<F: Float + BtapeThreadLocal>(
f: impl FnOnce(&[BReverse<F>]) -> Vec<BReverse<F>>,
x: &Array1<F>,
) -> (Array1<F>, crate::sparse::JacobianSparsityPattern, Vec<F>) {
let xs = to_vec(x);
let (outputs, pattern, values) = crate::api::sparse_jacobian(f, &xs);
(Array1::from_vec(outputs), pattern, values)
}