use crate::eq_mult::r#trait::EqMultCalculator;
use crate::init::r#trait::IterateInitializer;
use crate::ipopt_cq::IpoptCqHandle;
use crate::ipopt_data::IpoptDataHandle;
use crate::ipopt_nlp::IpoptNlp;
use crate::iterates_vector::IteratesVector;
use crate::kkt::aug_system_solver::{AugSysCoeffs, AugSysRhs, AugSysSol, AugSystemSolver};
use pounce_common::types::{Index, Number};
use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
use pounce_linalg::Vector;
use std::cell::RefCell;
use std::rc::Rc;
pub struct DefaultIterateInitializer {
pub bound_push: Number,
pub bound_frac: Number,
pub slack_bound_push: Number,
pub slack_bound_frac: Number,
pub constr_mult_init_max: Number,
pub bound_mult_init_val: Number,
pub bound_mult_init_method: String,
pub eq_mult_calculator: Option<Box<dyn EqMultCalculator>>,
pub least_square_init_primal: bool,
}
impl Default for DefaultIterateInitializer {
fn default() -> Self {
Self {
bound_push: 1e-2,
bound_frac: 1e-2,
slack_bound_push: 1e-2,
slack_bound_frac: 1e-2,
constr_mult_init_max: 1e3,
bound_mult_init_val: 1.0,
bound_mult_init_method: "constant".into(),
eq_mult_calculator: None,
least_square_init_primal: false,
}
}
}
impl DefaultIterateInitializer {
pub fn new() -> Self {
Self::default()
}
pub fn with_eq_mult_calculator(eq_mult: Box<dyn EqMultCalculator>) -> Self {
Self {
eq_mult_calculator: Some(eq_mult),
..Self::default()
}
}
fn calculate_least_square_primals(
&self,
cq: &IpoptCqHandle,
_nlp: &Rc<RefCell<dyn IpoptNlp>>,
aug_solver: &mut dyn AugSystemSolver,
n_x: Index,
) -> Option<Rc<dyn Vector>> {
let cq_ref = cq.borrow();
let curr_c = cq_ref.curr_c();
let curr_d = cq_ref.curr_d();
let j_c = cq_ref.curr_jac_c();
let j_d = cq_ref.curr_jac_d();
let zero_w = cq_ref.curr_exact_hessian();
drop(cq_ref);
let n_s = curr_d.dim();
let n_c = curr_c.dim();
let n_d = curr_d.dim();
let mut rhs_x = DenseVectorSpace::new(n_x).make_new_dense();
rhs_x.set(0.0);
let mut rhs_s = DenseVectorSpace::new(n_s).make_new_dense();
rhs_s.set(0.0);
let mut rhs_c_v = curr_c.make_new();
rhs_c_v.copy(&*curr_c);
let mut rhs_d_v = curr_d.make_new();
rhs_d_v.copy(&*curr_d);
let mut sol_x = DenseVectorSpace::new(n_x).make_new_dense();
let mut sol_s = DenseVectorSpace::new(n_s).make_new_dense();
let mut sol_c = DenseVectorSpace::new(n_c).make_new_dense();
let mut sol_d = DenseVectorSpace::new(n_d).make_new_dense();
let coeffs = AugSysCoeffs {
w: Some(&*zero_w),
w_factor: 0.0,
d_x: None,
delta_x: 1.0,
d_s: None,
delta_s: 1.0,
j_c: &*j_c,
d_c: None,
delta_c: 1e-8,
j_d: &*j_d,
d_d: None,
delta_d: 1e-8,
};
let aug_rhs = AugSysRhs {
rhs_x: &rhs_x,
rhs_s: &rhs_s,
rhs_c: &*rhs_c_v,
rhs_d: &*rhs_d_v,
};
let mut sol = AugSysSol {
sol_x: &mut sol_x,
sol_s: &mut sol_s,
sol_c: &mut sol_c,
sol_d: &mut sol_d,
};
let num_eq = n_c + n_d;
let check_neg = aug_solver.provides_inertia();
let status = aug_solver.solve(&coeffs, &aug_rhs, &mut sol, check_neg, num_eq);
if !matches!(status, pounce_linsol::ESymSolverStatus::Success) {
return None;
}
sol_x.scal(-1.0);
Some(Rc::new(sol_x))
}
pub fn push_to_interior(
bound_push: Number,
bound_frac: Number,
x: Number,
lower: Option<Number>,
upper: Option<Number>,
) -> Number {
match (lower, upper) {
(Some(lo), Some(hi)) => {
let span = hi - lo;
let p_l = (bound_push * lo.abs().max(1.0)).min(bound_frac * span);
let p_u = (bound_push * hi.abs().max(1.0)).min(bound_frac * span);
x.max(lo + p_l).min(hi - p_u)
}
(Some(lo), None) => {
let p_l = bound_push * lo.abs().max(1.0);
x.max(lo + p_l)
}
(None, Some(hi)) => {
let p_u = bound_push * hi.abs().max(1.0);
x.min(hi - p_u)
}
(None, None) => x,
}
}
}
impl IterateInitializer for DefaultIterateInitializer {
fn set_initial_iterates(
&mut self,
data: &IpoptDataHandle,
cq: &IpoptCqHandle,
nlp: &Rc<RefCell<dyn IpoptNlp>>,
aug_solver: &mut dyn AugSystemSolver,
) -> bool {
let curr_template = match data.borrow().curr.clone() {
Some(c) => c,
None => return false,
};
let n_x = curr_template.x.dim();
let n_s = curr_template.s.dim();
let n_yc = curr_template.y_c.dim();
let n_yd = curr_template.y_d.dim();
let n_zl = curr_template.z_l.dim();
let n_zu = curr_template.z_u.dim();
let n_vl = curr_template.v_l.dim();
let n_vu = curr_template.v_u.dim();
let mut x = DenseVectorSpace::new(n_x).make_new_dense();
nlp.borrow_mut().get_starting_x(&mut x);
if self.least_square_init_primal && (n_yc + n_yd) > 0 {
let mut x_stage = DenseVectorSpace::new(n_x).make_new_dense();
x_stage.copy(&x);
let mut s_zero = DenseVectorSpace::new(n_s).make_new_dense();
s_zero.set(0.0);
let mut y_c_zero = DenseVectorSpace::new(n_yc).make_new_dense();
y_c_zero.set(0.0);
let mut y_d_zero = DenseVectorSpace::new(n_yd).make_new_dense();
y_d_zero.set(0.0);
let mut z_l_zero = DenseVectorSpace::new(n_zl).make_new_dense();
z_l_zero.set(0.0);
let mut z_u_zero = DenseVectorSpace::new(n_zu).make_new_dense();
z_u_zero.set(0.0);
let mut v_l_zero = DenseVectorSpace::new(n_vl).make_new_dense();
v_l_zero.set(0.0);
let mut v_u_zero = DenseVectorSpace::new(n_vu).make_new_dense();
v_u_zero.set(0.0);
let stage_iv = IteratesVector::new(
Rc::new(x_stage),
Rc::new(s_zero),
Rc::new(y_c_zero),
Rc::new(y_d_zero),
Rc::new(z_l_zero),
Rc::new(z_u_zero),
Rc::new(v_l_zero),
Rc::new(v_u_zero),
);
data.borrow_mut().set_curr(stage_iv);
if let Some(x_ls) = self.calculate_least_square_primals(cq, nlp, aug_solver, n_x) {
x.copy(&*x_ls);
}
}
{
let nlp_ref = nlp.borrow();
push_x_into_interior(
&mut x,
&*nlp_ref.px_l(),
nlp_ref.x_l(),
&*nlp_ref.px_u(),
nlp_ref.x_u(),
self.bound_push,
self.bound_frac,
);
}
let mut s = DenseVectorSpace::new(n_s).make_new_dense();
nlp.borrow_mut().eval_d(&x, &mut s);
{
let nlp_ref = nlp.borrow();
push_x_into_interior(
&mut s,
&*nlp_ref.pd_l(),
nlp_ref.d_l(),
&*nlp_ref.pd_u(),
nlp_ref.d_u(),
self.slack_bound_push,
self.slack_bound_frac,
);
}
let mut y_c = DenseVectorSpace::new(n_yc).make_new_dense();
let mut y_d = DenseVectorSpace::new(n_yd).make_new_dense();
if self.bound_mult_init_method == "constant" {
y_c.set(0.0);
y_d.set(0.0);
} else {
nlp.borrow_mut().get_starting_y(&mut y_c, &mut y_d);
cap_constraint_multipliers(&mut y_c, self.constr_mult_init_max);
cap_constraint_multipliers(&mut y_d, self.constr_mult_init_max);
}
let mut z_l = DenseVectorSpace::new(n_zl).make_new_dense();
let mut z_u = DenseVectorSpace::new(n_zu).make_new_dense();
let mut v_l = DenseVectorSpace::new(n_vl).make_new_dense();
let mut v_u = DenseVectorSpace::new(n_vu).make_new_dense();
z_l.set(self.bound_mult_init_val);
z_u.set(self.bound_mult_init_val);
v_l.set(self.bound_mult_init_val);
v_u.set(self.bound_mult_init_val);
let iv = IteratesVector::new(
Rc::new(x),
Rc::new(s),
Rc::new(y_c),
Rc::new(y_d),
Rc::new(z_l),
Rc::new(z_u),
Rc::new(v_l),
Rc::new(v_u),
);
let n_x_dim = iv.x.dim();
data.borrow_mut().set_curr(iv);
if n_yc != n_x_dim
&& self.constr_mult_init_max > 0.0
&& (n_yc + n_yd) > 0
&& self.eq_mult_calculator.is_some()
{
let mut new_y_c = DenseVectorSpace::new(n_yc).make_new_dense();
let mut new_y_d = DenseVectorSpace::new(n_yd).make_new_dense();
let calc = self.eq_mult_calculator.as_mut().unwrap();
let ok = calc.calculate_y_eq(data, cq, nlp, aug_solver, &mut new_y_c, &mut new_y_d);
if !ok {
data.borrow_mut().append_info_string("y0");
} else {
let yinitnrm = new_y_c.amax().max(new_y_d.amax());
if yinitnrm > self.constr_mult_init_max {
data.borrow_mut().append_info_string("yc");
} else {
let curr = data.borrow().curr.clone();
if let Some(c) = curr {
let new_iv = IteratesVector::new(
c.x.clone(),
c.s.clone(),
Rc::new(new_y_c),
Rc::new(new_y_d),
c.z_l.clone(),
c.z_u.clone(),
c.v_l.clone(),
c.v_u.clone(),
);
let mut d = data.borrow_mut();
d.set_curr(new_iv);
d.append_info_string("y");
}
}
}
}
true
}
}
pub(crate) fn push_x_into_interior(
x: &mut DenseVector,
px_l: &dyn pounce_linalg::Matrix,
x_l: &dyn Vector,
px_u: &dyn pounce_linalg::Matrix,
x_u: &dyn Vector,
bound_push: Number,
bound_frac: Number,
) {
let n = x.dim() as usize;
let mut lower = vec![None; n];
let mut upper = vec![None; n];
expand_packed_into_dense(px_l, x_l, &mut lower);
expand_packed_into_dense(px_u, x_u, &mut upper);
let xs = x.values_mut();
for (i, xi) in xs.iter_mut().enumerate() {
*xi = DefaultIterateInitializer::push_to_interior(
bound_push, bound_frac, *xi, lower[i], upper[i],
);
}
}
fn expand_packed_into_dense(
p: &dyn pounce_linalg::Matrix,
b_packed: &dyn Vector,
out: &mut [Option<Number>],
) {
use pounce_linalg::expansion_matrix::ExpansionMatrix;
let dim_packed = b_packed.dim() as usize;
if dim_packed == 0 {
return;
}
if let Some(em) = p.as_any().downcast_ref::<ExpansionMatrix>() {
let rows = em.expanded_pos_indices();
let Some(packed) = b_packed.as_any().downcast_ref::<DenseVector>() else {
unreachable!("expansion-matrix bound vec must be DenseVector")
};
let vals = packed.values();
for k in 0..dim_packed {
let row = rows[k] as usize;
out[row] = Some(vals[k]);
}
} else {
let n_full = out.len() as i32;
let mut tmp = DenseVectorSpace::new(n_full).make_new_dense();
for k in 0..dim_packed {
let mut e_k = DenseVectorSpace::new(b_packed.dim()).make_new_dense();
e_k.values_mut()[k] = 1.0;
tmp.set(0.0);
p.mult_vector(1.0, &e_k, 0.0, &mut tmp);
let Some(packed) = b_packed.as_any().downcast_ref::<DenseVector>() else {
unreachable!("packed bound vec must be DenseVector")
};
for (i, &t) in tmp.values().iter().enumerate() {
if t == 1.0 {
out[i] = Some(packed.values()[k]);
}
}
}
}
}
fn cap_constraint_multipliers(y: &mut DenseVector, max: Number) {
for v in y.values_mut() {
if *v > max {
*v = max;
} else if *v < -max {
*v = -max;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interior_point_left_alone() {
let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 5.0, Some(0.0), Some(10.0));
assert!((v - 5.0).abs() < 1e-15);
}
#[test]
fn point_at_lower_bound_pushed_in() {
let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 0.0, Some(0.0), Some(10.0));
assert!((v - 0.01).abs() < 1e-15);
}
#[test]
fn point_at_upper_bound_pushed_in() {
let v =
DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 10.0, Some(0.0), Some(10.0));
assert!((v - 9.9).abs() < 1e-15);
}
#[test]
fn point_below_lower_bound_clamped() {
let v =
DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, -5.0, Some(0.0), Some(10.0));
assert!((v - 0.01).abs() < 1e-15);
}
#[test]
fn lower_only_pushed_by_max_abs() {
let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, -100.0, Some(-100.0), None);
assert!((v - -99.0).abs() < 1e-13);
}
#[test]
fn upper_only_pushed_by_max_abs() {
let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 50.0, None, Some(50.0));
assert!((v - 49.5).abs() < 1e-13);
}
#[test]
fn free_variable_unchanged() {
let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 42.0, None, None);
assert_eq!(v, 42.0);
}
#[test]
fn narrow_interval_uses_bound_frac_branch() {
let v = DefaultIterateInitializer::push_to_interior(1e-2, 1e-2, 0.0, Some(0.0), Some(1e-4));
assert!((v - 1e-6).abs() < 1e-18);
}
}