use crate::{
constraints,
continuation::homotopy_problem::HomotopyProblem,
continuation::ContinuationMode,
continuation::HomotopySolverStatus,
core::{panoc, ExitStatus, Optimizer, Problem},
matrix_operations, SolverError,
};
const DEFAULT_CONSTRAINT_TOLERANCE: f64 = 1e-4;
const DEFAULT_MAX_OUTER_ITERATIONS: usize = 10;
const DEFAULT_MAX_INNER_ITERATIONS: usize = 500;
pub struct HomotopyOptimizer<
'a,
ParametricPenaltyFunctionType,
ParametricGradientType,
ConstraintType,
ParametricCostType,
> where
ParametricPenaltyFunctionType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), SolverError>,
ParametricGradientType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), SolverError>,
ParametricCostType: Fn(&[f64], &[f64], &mut f64) -> Result<(), SolverError>,
ConstraintType: constraints::Constraint,
{
homotopy_problem: &'a HomotopyProblem<
ParametricPenaltyFunctionType,
ParametricGradientType,
ConstraintType,
ParametricCostType,
>,
panoc_cache: &'a mut panoc::PANOCCache,
constraint_tolerance: f64,
max_outer_iterations: usize,
max_inner_iterations: usize,
max_duration: Option<std::time::Duration>,
}
impl<
'a,
ParametricPenaltyFunctionType,
ParametricGradientType,
ConstraintType,
ParametricCostType,
>
HomotopyOptimizer<
'a,
ParametricPenaltyFunctionType,
ParametricGradientType,
ConstraintType,
ParametricCostType,
>
where
ParametricPenaltyFunctionType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), SolverError>,
ParametricGradientType: Fn(&[f64], &[f64], &mut [f64]) -> Result<(), SolverError>,
ParametricCostType: Fn(&[f64], &[f64], &mut f64) -> Result<(), SolverError>,
ConstraintType: constraints::Constraint,
{
pub fn new(
homotopy_problem: &'a HomotopyProblem<
ParametricPenaltyFunctionType,
ParametricGradientType,
ConstraintType,
ParametricCostType,
>,
panoc_cache: &'a mut panoc::PANOCCache,
) -> HomotopyOptimizer<
'a,
ParametricPenaltyFunctionType,
ParametricGradientType,
ConstraintType,
ParametricCostType,
> {
HomotopyOptimizer {
homotopy_problem: homotopy_problem,
panoc_cache: panoc_cache,
constraint_tolerance: DEFAULT_CONSTRAINT_TOLERANCE,
max_outer_iterations: DEFAULT_MAX_OUTER_ITERATIONS,
max_inner_iterations: DEFAULT_MAX_INNER_ITERATIONS,
max_duration: None,
}
}
fn initialize_param(&self, q_augmented_params: &mut [f64]) {
let homotopy_problem = &self.homotopy_problem;
let idx_list = &homotopy_problem.idx;
let from_list = &homotopy_problem.from;
for (i, from_value) in idx_list.iter().zip(from_list.iter()) {
q_augmented_params[*i] = *from_value;
}
}
pub fn with_constraint_tolerance(mut self, constraint_tolerance: f64) -> Self {
self.constraint_tolerance = constraint_tolerance;
self
}
pub fn with_max_outer_iterations(mut self, max_outer_iterations: usize) -> Self {
self.max_outer_iterations = max_outer_iterations;
self
}
pub fn with_max_inner_iterations(mut self, max_inner_iterations: usize) -> Self {
self.max_inner_iterations = max_inner_iterations;
self
}
pub fn with_max_duration(&mut self, max_duration: std::time::Duration) -> &Self {
self.max_duration = Some(max_duration);
self
}
fn update_continuation_parameters(&self, p_: &mut [f64]) {
let homotopy_problem = &self.homotopy_problem;
let idx_list = &homotopy_problem.idx;
let transition_list = &homotopy_problem.transition_mode;
for (i, transition_mode) in idx_list.iter().zip(transition_list.iter()) {
match transition_mode {
ContinuationMode::Arithmetic(s) => p_[*i] += s,
ContinuationMode::Geometric(s) => p_[*i] *= s,
_ => (),
}
}
}
pub fn solve(
&'a mut self,
u: &mut [f64],
q_augmented_param: &[f64],
) -> Result<HomotopySolverStatus, SolverError> {
let now = std::time::Instant::now();
let mut q_augmented_param_vec: Vec<f64> = q_augmented_param.to_vec();
self.initialize_param(&mut q_augmented_param_vec);
let mut last_norm_fpr: f64 = 1.;
let mut num_outer_iterations = 0;
let mut num_inner_iterations = 0;
let num_penalty_constraints = self.homotopy_problem.num_penalty_constraints;
let mut constraint_values: Vec<f64> = vec![0.0; std::cmp::max(1, num_penalty_constraints)];
let mut available_time_left = self.max_duration;
let mut exit_status = ExitStatus::Converged;
for _iter_outer in 1..=self.max_outer_iterations {
if let Some(max_duration) = self.max_duration {
available_time_left = max_duration.checked_sub(now.elapsed());
if None == available_time_left {
exit_status = ExitStatus::NotConvergedOutOfTime;
break;
}
}
num_outer_iterations += 1;
let homotopy_problem = &self.homotopy_problem;
let f_ = |u: &[f64], cost: &mut f64| -> Result<(), SolverError> {
(homotopy_problem.parametric_cost)(u, &q_augmented_param_vec, cost)
};
let df_ = |u: &[f64], grad: &mut [f64]| -> Result<(), SolverError> {
(homotopy_problem.parametric_gradient)(u, &q_augmented_param_vec, grad)
};
let inner_problem = Problem::new(&self.homotopy_problem.constraints, df_, f_);
let mut inner_panoc = panoc::PANOCOptimizer::new(inner_problem, &mut self.panoc_cache)
.with_max_iter(self.max_inner_iterations);
if available_time_left != None {
inner_panoc.with_max_duration(available_time_left.unwrap());
}
let status = inner_panoc.solve(u).unwrap();
num_inner_iterations += status.iterations();
last_norm_fpr = status.norm_fpr();
exit_status = status.exit_status();
(homotopy_problem.penalty_function)(u, &q_augmented_param_vec, &mut constraint_values)?;
let continue_outer_iterations = constraint_values
.iter()
.any(|&ci| ci.abs() > self.constraint_tolerance);
if !continue_outer_iterations {
break;
} else {
self.update_continuation_parameters(&mut q_augmented_param_vec);
exit_status = ExitStatus::NotConvergedIterations;
}
}
let max_constraint_violation = matrix_operations::norm_inf(&constraint_values);
Ok(HomotopySolverStatus::new(
exit_status,
num_outer_iterations,
num_inner_iterations,
last_norm_fpr,
max_constraint_violation,
now.elapsed(),
))
}
}