use nalgebra::{DMatrix, DVector};
use crate::fcn::FCNGradient;
use crate::gradient::{
AnalyticalGradientCalculator, InitialGradientCalculator, Numerical2PGradientCalculator,
};
use crate::minimum::error::MinimumError;
use crate::minimum::parameters::MinimumParameters;
use crate::minimum::seed::MinimumSeed;
use crate::minimum::state::MinimumState;
use crate::mn_fcn::MnFcn;
use crate::strategy::MnStrategy;
use crate::user_transformation::MnUserTransformation;
pub struct MigradSeedGenerator;
impl MigradSeedGenerator {
pub fn generate(
fcn: &MnFcn,
trafo: &MnUserTransformation,
strategy: &MnStrategy,
) -> MinimumSeed {
let n = trafo.variable_parameters();
let eps2 = trafo.precision().eps2();
let int_values = trafo.initial_internal_values();
let int_vec = DVector::from_vec(int_values.clone());
let fval = fcn.call(&int_values);
let params = MinimumParameters::new(int_vec, fval);
let heuristic_calc = InitialGradientCalculator::new(*strategy);
let heuristic_grad = heuristic_calc.compute(fcn, ¶ms, trafo);
let numerical_calc = Numerical2PGradientCalculator::new(*strategy);
let gradient = numerical_calc.compute(fcn, ¶ms, trafo, &heuristic_grad);
let mut v0 = DMatrix::zeros(n, n);
for i in 0..n {
let g2i = gradient.g2()[i];
v0[(i, i)] = if g2i > eps2 { 1.0 / g2i } else { 1.0 };
}
let dcovar = 1.0; let error = MinimumError::new(v0, dcovar);
let edm = {
let g = gradient.grad();
let e = error.matrix();
0.5 * g.dot(&(e * g))
};
let state = MinimumState::new(params, error, gradient, edm, fcn.num_of_calls());
MinimumSeed::new(state, trafo.clone())
}
pub fn generate_with_gradient(
fcn: &dyn FCNGradient,
trafo: &MnUserTransformation,
_strategy: &MnStrategy,
) -> MinimumSeed {
let n = trafo.variable_parameters();
let eps2 = trafo.precision().eps2();
let int_values = trafo.initial_internal_values();
let int_vec = DVector::from_vec(int_values.clone());
let fval = fcn.value(&trafo.transform(&int_values));
let params = MinimumParameters::new(int_vec, fval);
let gradient = AnalyticalGradientCalculator::compute(fcn, trafo, ¶ms);
let mut v0 = DMatrix::zeros(n, n);
for i in 0..n {
let g2i = gradient.g2()[i];
v0[(i, i)] = if g2i > eps2 { 1.0 / g2i } else { 1.0 };
}
let dcovar = 1.0; let error = MinimumError::new(v0, dcovar);
let edm = {
let g = gradient.grad();
let e = error.matrix();
0.5 * g.dot(&(e * g))
};
let state = MinimumState::new(params, error, gradient, edm, 1);
MinimumSeed::new(state, trafo.clone())
}
pub fn call_with_analytical_gradient_calculator(
fcn: &dyn FCNGradient,
trafo: &MnUserTransformation,
strategy: &MnStrategy,
) -> MinimumSeed {
Self::generate_with_gradient(fcn, trafo, strategy)
}
}