use self::internal::*;
use super::cones::Cone;
use super::traits::*;
use crate::algebra::*;
use crate::timers::*;
#[repr(u32)]
#[derive(PartialEq, Clone, Debug, Copy)]
pub enum SolverStatus {
Unsolved,
Solved,
PrimalInfeasible,
DualInfeasible,
AlmostSolved,
AlmostPrimalInfeasible,
AlmostDualInfeasible,
MaxIterations,
MaxTime,
NumericalError,
InsufficientProgress,
}
impl SolverStatus {
pub(crate) fn is_infeasible(&self) -> bool {
matches!(
*self,
|SolverStatus::PrimalInfeasible| SolverStatus::DualInfeasible
| SolverStatus::AlmostPrimalInfeasible
| SolverStatus::AlmostDualInfeasible
)
}
pub(crate) fn is_errored(&self) -> bool {
matches!(
*self,
SolverStatus::NumericalError | SolverStatus::InsufficientProgress
)
}
}
#[repr(u32)]
#[derive(PartialEq, Clone, Debug, Copy)]
pub enum ScalingStrategy {
PrimalDual,
Dual,
}
#[repr(u32)]
#[derive(PartialEq, Clone, Debug, Copy)]
enum StrategyCheckpoint {
Update(ScalingStrategy), NoUpdate, Fail, }
impl std::fmt::Display for SolverStatus {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl Default for SolverStatus {
fn default() -> Self {
SolverStatus::Unsolved
}
}
pub struct Solver<D, V, R, K, C, I, SO, SE> {
pub data: D,
pub variables: V,
pub residuals: R,
pub kktsystem: K,
pub cones: C,
pub step_lhs: V,
pub step_rhs: V,
pub prev_vars: V,
pub info: I,
pub solution: SO,
pub settings: SE,
pub timers: Option<Timers>,
}
fn _print_banner(is_verbose: bool) {
if !is_verbose {
return;
}
println!("-------------------------------------------------------------");
println!(
" Clarabel.rs v{} - Clever Acronym \n",
crate::VERSION
);
println!(" (c) Paul Goulart ");
println!(" University of Oxford, 2022 ");
println!("-------------------------------------------------------------");
}
pub trait IPSolver<T, D, V, R, K, C, I, SO, SE> {
fn solve(&mut self);
}
impl<T, D, V, R, K, C, I, SO, SE> IPSolver<T, D, V, R, K, C, I, SO, SE>
for Solver<D, V, R, K, C, I, SO, SE>
where
T: FloatT,
D: ProblemData<T, V = V>,
V: Variables<T, D = D, R = R, C = C, SE = SE>,
R: Residuals<T, D = D, V = V>,
K: KKTSystem<T, D = D, V = V, C = C, SE = SE>,
C: Cone<T>,
I: Info<T, D = D, V = V, R = R, C = C, SE = SE>,
SO: Solution<T, D = D, V = V, I = I>,
SE: Settings<T>,
{
fn solve(&mut self) {
let mut iter: u32 = 0;
let mut σ = T::one();
let mut α = T::zero();
let mut μ;
let mut timers = self.timers.take().unwrap();
notimeit! {timers; {
_print_banner(self.settings.core().verbose);
self.info.print_configuration(&self.settings, &self.data, &self.cones);
self.info.print_status_header(&self.settings);
}}
self.info.reset(&mut timers);
timeit! {timers => "solve"; {
timeit!{timers => "default start"; {
self.default_start();
}}
timeit!{timers => "IP iteration"; {
let mut scaling = ScalingStrategy::PrimalDual;
loop {
self.residuals.update(&self.variables, &self.data);
μ = self.variables.calc_mu(&self.residuals, &self.cones);
self.info.save_scalars(μ, α, σ, iter);
self.info.update(
&self.data,
&self.variables,
&self.residuals,&timers);
notimeit!{timers; {
self.info.print_status(&self.settings);
}}
let isdone = self.info.check_termination(&self.residuals, &self.settings, iter);
if isdone{
match self.strategy_checkpoint_insufficient_progress(scaling){
StrategyCheckpoint::NoUpdate | StrategyCheckpoint::Fail => {break}
StrategyCheckpoint::Update(s) => {scaling = s; continue}
}
}
iter += 1;
self.variables.scale_cones(&mut self.cones,μ,scaling);
let mut is_kkt_solve_success : bool;
timeit!{timers => "kkt update"; {
is_kkt_solve_success = self.kktsystem.update(&self.data, &self.cones, &self.settings);
}}
self.step_rhs
.affine_step_rhs(&self.residuals, &self.variables, &self.cones);
timeit!{timers => "kkt solve"; {
is_kkt_solve_success = is_kkt_solve_success &&
self.kktsystem.solve(
&mut self.step_lhs,
&self.step_rhs,
&self.data,
&self.variables,
&self.cones,
"affine",
&self.settings,
);
}}
if is_kkt_solve_success {
α = self.get_step_length("affine",scaling);
σ = self.centering_parameter(α);
self.step_rhs.combined_step_rhs(
&self.residuals,
&self.variables,
&mut self.cones,
&mut self.step_lhs,
σ,
μ,
);
timeit!{timers => "kkt solve" ; {
is_kkt_solve_success =
self.kktsystem.solve(
&mut self.step_lhs,
&self.step_rhs,
&self.data,
&self.variables,
&self.cones,
"combined",
&self.settings,
);
}} }
match self.strategy_checkpoint_numerical_error(is_kkt_solve_success,scaling) {
StrategyCheckpoint::NoUpdate => {}
StrategyCheckpoint::Update(s) => {α = T::zero(); scaling = s; continue}
StrategyCheckpoint::Fail => {α = T::zero(); break}
}
α = self.get_step_length("combined",scaling);
match self.strategy_checkpoint_small_step(α, scaling) {
StrategyCheckpoint::NoUpdate => {}
StrategyCheckpoint::Update(s) => {α = T::zero(); scaling = s; continue}
StrategyCheckpoint::Fail => {α = T::zero(); break}
}
self.info.save_prev_iterate(&self.variables,&mut self.prev_vars);
self.variables.add_step(&self.step_lhs, α);
}
}}
}}
if α == T::zero() {
self.info.save_scalars(μ, α, σ, iter);
notimeit! {timers; {self.info.print_status(&self.settings);}}
}
self.info
.finalize(&self.residuals, &self.settings, &mut timers);
self.solution
.finalize(&self.data, &self.variables, &self.info);
self.info.print_footer(&self.settings);
self.timers.replace(timers);
}
}
mod internal {
use super::super::cones::Cone;
use super::super::traits::*;
use super::*;
pub(super) trait IPSolverInternals<T, D, V, R, K, C, I, SO, SE> {
fn default_start(&mut self);
fn centering_parameter(&self, α: T) -> T;
fn get_step_length(&self, steptype: &'static str, scaling: ScalingStrategy) -> T;
fn backtrack_step_to_barrier(&self, αinit: T) -> T;
fn strategy_checkpoint_insufficient_progress(
&mut self,
scaling: ScalingStrategy,
) -> StrategyCheckpoint;
fn strategy_checkpoint_numerical_error(
&mut self,
is_kkt_solve_success: bool,
scaling: ScalingStrategy,
) -> StrategyCheckpoint;
fn strategy_checkpoint_small_step(
&mut self,
α: T,
scaling: ScalingStrategy,
) -> StrategyCheckpoint;
}
impl<T, D, V, R, K, C, I, SO, SE> IPSolverInternals<T, D, V, R, K, C, I, SO, SE>
for Solver<D, V, R, K, C, I, SO, SE>
where
T: FloatT,
D: ProblemData<T, V = V>,
V: Variables<T, D = D, R = R, C = C, SE = SE>,
R: Residuals<T, D = D, V = V>,
K: KKTSystem<T, D = D, V = V, C = C, SE = SE>,
C: Cone<T>,
I: Info<T, D = D, V = V, R = R, C = C, SE = SE>,
SO: Solution<T, D = D, V = V, I = I>,
SE: Settings<T>,
{
fn default_start(&mut self) {
if self.cones.is_symmetric() {
self.cones.set_identity_scaling();
self.kktsystem
.update(&self.data, &self.cones, &self.settings);
self.kktsystem
.solve_initial_point(&mut self.variables, &self.data, &self.settings);
self.variables.symmetric_initialization(&self.cones);
} else {
self.variables.unit_initialization(&self.cones);
}
}
fn centering_parameter(&self, α: T) -> T {
T::powi(T::one() - α, 3)
}
fn get_step_length(&self, steptype: &'static str, scaling: ScalingStrategy) -> T {
let mut α = self.variables.calc_step_length(
&self.step_lhs,
&self.cones,
&self.settings,
steptype,
);
if !self.cones.is_symmetric()
&& steptype.eq("combined")
&& scaling == ScalingStrategy::Dual
{
let αinit = α;
α = self.backtrack_step_to_barrier(αinit);
}
α
}
fn backtrack_step_to_barrier(&self, αinit: T) -> T {
let backtrack = self.settings.core().linesearch_backtrack_step;
let mut α = αinit;
for _ in 0..50 {
let barrier = self.variables.barrier(&self.step_lhs, α, &self.cones);
if barrier < T::one() {
return α;
} else {
α = backtrack * α }
}
α
}
fn strategy_checkpoint_insufficient_progress(
&mut self,
scaling: ScalingStrategy,
) -> StrategyCheckpoint {
let output;
if self.info.get_status() != SolverStatus::InsufficientProgress {
output = StrategyCheckpoint::NoUpdate;
} else {
self.info
.reset_to_prev_iterate(&mut self.variables, &self.prev_vars);
if !self.cones.is_symmetric() && (scaling == ScalingStrategy::PrimalDual) {
self.info.set_status(SolverStatus::Unsolved);
output = StrategyCheckpoint::Update(ScalingStrategy::Dual);
} else {
output = StrategyCheckpoint::Fail;
}
}
output
}
fn strategy_checkpoint_numerical_error(
&mut self,
is_kkt_solve_success: bool,
scaling: ScalingStrategy,
) -> StrategyCheckpoint {
let output;
if is_kkt_solve_success {
output = StrategyCheckpoint::NoUpdate;
}
else if !self.cones.is_symmetric() && (scaling == ScalingStrategy::PrimalDual) {
output = StrategyCheckpoint::Update(ScalingStrategy::Dual);
} else {
self.info.set_status(SolverStatus::NumericalError);
output = StrategyCheckpoint::Fail;
}
output
}
fn strategy_checkpoint_small_step(
&mut self,
α: T,
scaling: ScalingStrategy,
) -> StrategyCheckpoint {
let output;
if !self.cones.is_symmetric()
&& scaling == ScalingStrategy::PrimalDual
&& α < self.settings.core().min_switch_step_length
{
output = StrategyCheckpoint::Update(ScalingStrategy::Dual);
} else if α <= T::min(T::zero(), self.settings.core().min_terminate_step_length) {
self.info.set_status(SolverStatus::InsufficientProgress);
output = StrategyCheckpoint::Fail;
} else {
output = StrategyCheckpoint::NoUpdate;
}
output
}
} }