use std::collections::HashMap;
use crate::numerical::elementary::eval_expr;
use crate::symbolic::core::Expr;
pub fn partial_derivative(
f: &Expr,
var: &str,
x: f64,
) -> Result<f64, String> {
let h = 1e-6;
let mut vars_plus = HashMap::new();
vars_plus.insert(var.to_string(), x + h);
let f_plus = eval_expr(f, &vars_plus)?;
let mut vars_minus = HashMap::new();
vars_minus.insert(var.to_string(), x - h);
let f_minus = eval_expr(f, &vars_minus)?;
Ok((f_plus - f_minus) / (2.0 * h))
}
pub fn gradient(
f: &Expr,
vars: &[&str],
point: &[f64],
) -> Result<Vec<f64>, String> {
if vars.len() != point.len() {
return Err("Number of variables must \
match number of point \
dimensions"
.to_string());
}
let mut grad = Vec::with_capacity(vars.len());
let h = 1e-6;
for i in 0..vars.len() {
let mut point_plus_h = point.to_vec();
point_plus_h[i] += h;
let mut point_minus_h = point.to_vec();
point_minus_h[i] -= h;
let f_plus_h = eval_at_point(f, vars, &point_plus_h)?;
let f_minus_h = eval_at_point(f, vars, &point_minus_h)?;
let partial_deriv = (f_plus_h - f_minus_h) / (2.0 * h);
grad.push(partial_deriv);
}
Ok(grad)
}
pub fn jacobian(
funcs: &[Expr],
vars: &[&str],
point: &[f64],
) -> Result<Vec<Vec<f64>>, String> {
let mut jac = Vec::with_capacity(funcs.len());
for f in funcs {
jac.push(gradient(f, vars, point)?);
}
Ok(jac)
}
pub fn hessian(
f: &Expr,
vars: &[&str],
point: &[f64],
) -> Result<Vec<Vec<f64>>, String> {
if vars.len() != point.len() {
return Err("Number of variables must \
match number of point \
dimensions"
.to_string());
}
let n = vars.len();
let h = 1e-4; let mut hess = vec![vec![0.0; n]; n];
for i in 0..n {
for j in 0..=i {
let val = if i == j {
let mut p_plus = point.to_vec();
p_plus[i] += h;
let mut p_minus = point.to_vec();
p_minus[i] -= h;
let f_plus = eval_at_point(f, vars, &p_plus)?;
let f_center = eval_at_point(f, vars, point)?;
let f_minus = eval_at_point(f, vars, &p_minus)?;
(2.0f64.mul_add(-f_center, f_plus) + f_minus) / (h * h)
} else {
let mut p_pp = point.to_vec();
p_pp[i] += h;
p_pp[j] += h;
let mut p_pm = point.to_vec();
p_pm[i] += h;
p_pm[j] -= h;
let mut p_mp = point.to_vec();
p_mp[i] -= h;
p_mp[j] += h;
let mut p_mm = point.to_vec();
p_mm[i] -= h;
p_mm[j] -= h;
let f_pp = eval_at_point(f, vars, &p_pp)?;
let f_pm = eval_at_point(f, vars, &p_pm)?;
let f_mp = eval_at_point(f, vars, &p_mp)?;
let f_mm = eval_at_point(f, vars, &p_mm)?;
(f_pp - f_pm - f_mp + f_mm) / (4.0 * h * h)
};
hess[i][j] = val;
hess[j][i] = val;
}
}
Ok(hess)
}
pub(crate) fn eval_at_point(
expr: &Expr,
vars: &[&str],
point: &[f64],
) -> Result<f64, String> {
let mut vars_map = HashMap::new();
for (i, &var) in vars.iter().enumerate() {
vars_map.insert(var.to_string(), point[i]);
}
eval_expr(expr, &vars_map)
}