mod bfgs;
mod gradient_descent;
mod lbfgsb;
mod nelder_mead;
pub use bfgs::bfgs;
pub use gradient_descent::gradient_descent;
pub use lbfgsb::{Bounds, lbfgsb};
pub use nelder_mead::nelder_mead;
use scivex_core::{Float, Tensor};
#[cfg_attr(
feature = "serde-support",
derive(serde::Serialize, serde::Deserialize)
)]
#[derive(Debug, Clone)]
pub struct MinimizeResult<T: Float> {
pub x: Tensor<T>,
pub f_val: T,
pub grad: Option<Tensor<T>>,
pub iterations: usize,
pub f_evals: usize,
pub g_evals: usize,
pub converged: bool,
}
#[cfg_attr(
feature = "serde-support",
derive(serde::Serialize, serde::Deserialize)
)]
#[derive(Debug, Clone)]
pub struct MinimizeOptions<T: Float> {
pub gtol: T,
pub xtol: T,
pub ftol: T,
pub max_iter: usize,
pub learning_rate: T,
}
impl<T: Float> Default for MinimizeOptions<T> {
fn default() -> Self {
Self {
gtol: T::from_f64(1e-8),
xtol: T::from_f64(1e-12),
ftol: T::from_f64(1e-12),
max_iter: 1000,
learning_rate: T::from_f64(0.01),
}
}
}
pub fn numerical_gradient<T: Float, F: Fn(&Tensor<T>) -> T>(f: &F, x: &Tensor<T>) -> Tensor<T> {
let n = x.numel();
let h = T::from_f64(1e-8);
let two_h = h * T::from_f64(2.0);
let mut grad_data = vec![T::zero(); n];
let x_data = x.as_slice();
let mut x_plus = x_data.to_vec();
let mut x_minus = x_data.to_vec();
for i in 0..n {
let orig = x_data[i];
x_plus[i] = orig + h;
x_minus[i] = orig - h;
let t_plus = Tensor::from_vec(x_plus.clone(), vec![n])
.expect("perturbed vector length matches original dimension n");
let t_minus = Tensor::from_vec(x_minus.clone(), vec![n])
.expect("perturbed vector length matches original dimension n");
grad_data[i] = (f(&t_plus) - f(&t_minus)) / two_h;
x_plus[i] = orig;
x_minus[i] = orig;
}
Tensor::from_vec(grad_data, vec![n]).expect("gradient vector length matches dimension n")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_numerical_gradient_quadratic() {
let f = |x: &Tensor<f64>| {
let s = x.as_slice();
s[0] * s[0] + s[1] * s[1]
};
let x = Tensor::from_vec(vec![3.0, 4.0], vec![2]).unwrap();
let g = numerical_gradient(&f, &x);
let gs = g.as_slice();
assert!((gs[0] - 6.0).abs() < 1e-5, "got {}", gs[0]);
assert!((gs[1] - 8.0).abs() < 1e-5, "got {}", gs[1]);
}
}