use echidna::{BytecodeTape, Float};
pub trait Objective<F: num_traits::Float> {
fn dim(&self) -> usize;
fn eval_grad(&mut self, x: &[F]) -> (F, Vec<F>);
fn eval_hessian(&mut self, x: &[F]) -> (F, Vec<F>, Vec<Vec<F>>) {
let _ = x;
unimplemented!("eval_hessian not implemented for this objective")
}
fn hvp(&mut self, x: &[F], v: &[F]) -> (Vec<F>, Vec<F>) {
let _ = (x, v);
unimplemented!("hvp not implemented for this objective")
}
}
pub struct TapeObjective<F: Float> {
tape: BytecodeTape<F>,
func_evals: usize,
}
impl<F: Float> TapeObjective<F> {
pub fn new(tape: BytecodeTape<F>) -> Self {
TapeObjective {
tape,
func_evals: 0,
}
}
pub fn func_evals(&self) -> usize {
self.func_evals
}
pub fn tape(&self) -> &BytecodeTape<F> {
&self.tape
}
}
impl<F: Float> Objective<F> for TapeObjective<F> {
fn dim(&self) -> usize {
self.tape.num_inputs()
}
fn eval_grad(&mut self, x: &[F]) -> (F, Vec<F>) {
self.func_evals += 1;
let grad = self.tape.gradient(x);
let value = self.tape.output_value();
(value, grad)
}
fn eval_hessian(&mut self, x: &[F]) -> (F, Vec<F>, Vec<Vec<F>>) {
self.func_evals += 1;
self.tape.hessian(x)
}
fn hvp(&mut self, x: &[F], v: &[F]) -> (Vec<F>, Vec<F>) {
self.func_evals += 1;
self.tape.hvp(x, v)
}
}