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::AugSystemSolver;
use pounce_common::types::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>>,
}
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,
}
}
}
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()
}
}
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);
{
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);
}
}