use pounce_algorithm::init::r#trait::IterateInitializer;
use pounce_algorithm::ipopt_cq::IpoptCqHandle;
use pounce_algorithm::ipopt_data::IpoptDataHandle;
use pounce_algorithm::ipopt_nlp::IpoptNlp;
use pounce_algorithm::iterates_vector::IteratesVector;
use pounce_algorithm::kkt::aug_system_solver::AugSystemSolver;
use pounce_common::types::{Index, Number};
use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
use pounce_linalg::{CompoundVector, CompoundVectorSpace, Vector};
use std::cell::RefCell;
use std::rc::Rc;
use crate::resto_nlp::{BLOCK_N_C, BLOCK_N_D, BLOCK_P_C, BLOCK_P_D, BLOCK_X};
pub struct OuterIterateSnapshot {
pub mu: Number,
pub s: Rc<dyn Vector>,
pub z_l: Rc<dyn Vector>,
pub z_u: Rc<dyn Vector>,
pub v_l: Rc<dyn Vector>,
pub v_u: Rc<dyn Vector>,
pub c_vec: Rc<dyn Vector>,
pub d_minus_s_vec: Rc<dyn Vector>,
}
pub struct RestoIterateInitializer {
pub rho: Number,
pub n_orig: Index,
pub m_eq: Index,
pub m_ineq: Index,
pub x_ref_vals: Vec<Number>,
pub outer: Option<OuterIterateSnapshot>,
}
impl RestoIterateInitializer {
pub fn new() -> Self {
Self::default()
}
pub fn with_dims(n_orig: Index, m_eq: Index, m_ineq: Index, x_ref_vals: Vec<Number>) -> Self {
Self {
n_orig,
m_eq,
m_ineq,
x_ref_vals,
..Self::default()
}
}
pub fn set_outer_snapshot(&mut self, snap: OuterIterateSnapshot) {
self.outer = Some(snap);
}
pub fn with_outer_snapshot(mut self, snap: OuterIterateSnapshot) -> Self {
self.outer = Some(snap);
self
}
pub fn with_rho(mut self, rho: Number) -> Self {
self.rho = rho;
self
}
}
impl Default for RestoIterateInitializer {
fn default() -> Self {
Self {
rho: 1e3,
n_orig: 0,
m_eq: 0,
m_ineq: 0,
x_ref_vals: Vec::new(),
outer: None,
}
}
}
impl IterateInitializer for RestoIterateInitializer {
fn set_initial_iterates(
&mut self,
data: &IpoptDataHandle,
_cq: &IpoptCqHandle,
nlp: &Rc<RefCell<dyn IpoptNlp>>,
_aug_solver: &mut dyn AugSystemSolver,
) -> bool {
let Some(snap) = self.outer.as_ref() else {
return false;
};
let nlp_ref = nlp.borrow();
let xl_compound = nlp_ref
.x_l()
.as_any()
.downcast_ref::<CompoundVector>()
.expect("RestoIpoptNlp::x_l must be a 5-block CompoundVector");
let n_xl_orig = xl_compound.comp(0).dim();
let n_xu_orig = nlp_ref.x_u().dim();
let n_dl = nlp_ref.d_l().dim();
let n_du = nlp_ref.d_u().dim();
drop(nlp_ref);
let _n_orig = self.n_orig;
let m_eq = self.m_eq;
let m_ineq = self.m_ineq;
let x_ref_vals = self.x_ref_vals.clone();
let x_space = build_x_space(self.n_orig, m_eq, m_ineq);
let c_amax = snap.c_vec.amax();
let d_minus_s_amax = snap.d_minus_s_vec.amax();
let resto_mu = restoration_mu(snap.mu, c_amax, d_minus_s_amax);
data.borrow_mut().curr_mu = resto_mu;
let mut x = CompoundVector::new(Rc::clone(&x_space));
downcast_dense_mut(x.comp_mut(BLOCK_X)).set_values(&x_ref_vals);
let c_vals = snap
.c_vec
.as_any()
.downcast_ref::<DenseVector>()
.map(|d| d.expanded_values())
.unwrap_or_else(|| vec![0.0; m_eq as usize]);
let dms_vals = snap
.d_minus_s_vec
.as_any()
.downcast_ref::<DenseVector>()
.map(|d| d.expanded_values())
.unwrap_or_else(|| vec![0.0; m_ineq as usize]);
let mut nc_vals = vec![0.0; m_eq as usize];
let mut pc_vals = vec![0.0; m_eq as usize];
for i in 0..m_eq as usize {
let (n, p) = init_slack_pair(c_vals[i], resto_mu, self.rho);
nc_vals[i] = n;
pc_vals[i] = p;
}
let mut nd_vals = vec![0.0; m_ineq as usize];
let mut pd_vals = vec![0.0; m_ineq as usize];
for i in 0..m_ineq as usize {
let (n, p) = init_slack_pair(dms_vals[i], resto_mu, self.rho);
nd_vals[i] = n;
pd_vals[i] = p;
}
downcast_dense_mut(x.comp_mut(BLOCK_N_C)).set_values(&nc_vals);
downcast_dense_mut(x.comp_mut(BLOCK_P_C)).set_values(&pc_vals);
downcast_dense_mut(x.comp_mut(BLOCK_N_D)).set_values(&nd_vals);
downcast_dense_mut(x.comp_mut(BLOCK_P_D)).set_values(&pd_vals);
let mut s = DenseVectorSpace::new(m_ineq).make_new_dense();
let s_outer = snap
.s
.as_any()
.downcast_ref::<DenseVector>()
.map(|d| d.expanded_values())
.unwrap_or_else(|| vec![0.0; m_ineq as usize]);
s.set_values(&s_outer);
let mut y_c = DenseVectorSpace::new(m_eq).make_new_dense();
let mut y_d = DenseVectorSpace::new(m_ineq).make_new_dense();
y_c.set_values(&vec![0.0; m_eq as usize]);
y_d.set_values(&vec![0.0; m_ineq as usize]);
let z_l_total = n_xl_orig + 2 * m_eq + 2 * m_ineq;
let z_l_space = build_z_l_space(n_xl_orig, m_eq, m_ineq, z_l_total);
let mut z_l = CompoundVector::new(z_l_space);
let outer_zl_vals = snap
.z_l
.as_any()
.downcast_ref::<DenseVector>()
.map(|d| d.expanded_values())
.unwrap_or_else(|| vec![0.0; n_xl_orig as usize]);
let mut zl0 = vec![0.0; n_xl_orig as usize];
for (i, &v) in outer_zl_vals.iter().enumerate() {
zl0[i] = self.rho.min(v);
}
downcast_dense_mut(z_l.comp_mut(0)).set_values(&zl0);
downcast_dense_mut(z_l.comp_mut(1)).set_values(÷_safe(resto_mu, &nc_vals));
downcast_dense_mut(z_l.comp_mut(2)).set_values(÷_safe(resto_mu, &pc_vals));
downcast_dense_mut(z_l.comp_mut(3)).set_values(÷_safe(resto_mu, &nd_vals));
downcast_dense_mut(z_l.comp_mut(4)).set_values(÷_safe(resto_mu, &pd_vals));
let mut z_u = DenseVectorSpace::new(n_xu_orig).make_new_dense();
let outer_zu_vals = snap
.z_u
.as_any()
.downcast_ref::<DenseVector>()
.map(|d| d.expanded_values())
.unwrap_or_else(|| vec![0.0; n_xu_orig as usize]);
let mut zu = vec![0.0; n_xu_orig as usize];
for (i, &v) in outer_zu_vals.iter().enumerate() {
zu[i] = self.rho.min(v);
}
z_u.set_values(&zu);
let mut v_l = DenseVectorSpace::new(n_dl).make_new_dense();
let outer_vl_vals = snap
.v_l
.as_any()
.downcast_ref::<DenseVector>()
.map(|d| d.expanded_values())
.unwrap_or_else(|| vec![0.0; n_dl as usize]);
let mut vl = vec![0.0; n_dl as usize];
for (i, &v) in outer_vl_vals.iter().enumerate() {
vl[i] = self.rho.min(v);
}
v_l.set_values(&vl);
let mut v_u = DenseVectorSpace::new(n_du).make_new_dense();
let outer_vu_vals = snap
.v_u
.as_any()
.downcast_ref::<DenseVector>()
.map(|d| d.expanded_values())
.unwrap_or_else(|| vec![0.0; n_du as usize]);
let mut vu = vec![0.0; n_du as usize];
for (i, &v) in outer_vu_vals.iter().enumerate() {
vu[i] = self.rho.min(v);
}
v_u.set_values(&vu);
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),
);
if std::env::var_os("POUNCE_DBG_RESTO_INIT").is_some() {
use pounce_linalg::Vector;
fn dump3(label: &str, v: &dyn Vector) {
tracing::debug!(target: "pounce::restoration",
"[PN_RESTO_INIT] {} amax={:.17e} asum={:.17e} nrm2={:.17e}",
label,
v.amax(),
v.asum(),
v.nrm2()
);
}
fn dump2(label: &str, v: &dyn Vector) {
tracing::debug!(target: "pounce::restoration",
"[PN_RESTO_INIT] {} amax={:.17e} asum={:.17e}",
label,
v.amax(),
v.asum()
);
}
let cx = iv.x.as_any().downcast_ref::<CompoundVector>().unwrap();
let czl = iv.z_l.as_any().downcast_ref::<CompoundVector>().unwrap();
tracing::debug!(target: "pounce::restoration",
"[PN_RESTO_INIT] resto_mu={:.17e} rho={:.17e}",
resto_mu, self.rho
);
dump3("x_orig ", &*cx.comp(0));
dump3("nc ", &*cx.comp(1));
dump3("pc ", &*cx.comp(2));
dump3("nd ", &*cx.comp(3));
dump3("pd ", &*cx.comp(4));
dump2("zL_orig ", &*czl.comp(0));
dump2("zL_nc ", &*czl.comp(1));
dump2("zL_pc ", &*czl.comp(2));
dump2("zL_nd ", &*czl.comp(3));
dump2("zL_pd ", &*czl.comp(4));
tracing::debug!(target: "pounce::restoration",
"[PN_RESTO_INIT] y_c amax={:.17e} y_d amax={:.17e}",
iv.y_c.amax(),
iv.y_d.amax()
);
}
data.borrow_mut().set_curr(iv);
true
}
}
pub fn restoration_mu(curr_mu: f64, c_amax: f64, d_minus_s_amax: f64) -> f64 {
curr_mu.max(c_amax).max(d_minus_s_amax)
}
pub fn solve_quadratic_elem(a: f64, b: f64) -> f64 {
(a * a + b).sqrt() + a
}
pub fn init_slack_pair(c: f64, mu_r: f64, rho: f64) -> (f64, f64) {
let half = mu_r / (2.0 * rho);
let a = half - 0.5 * c;
let b = c * half;
let n = solve_quadratic_elem(a, b);
let p = c + n;
(n, p)
}
fn build_x_space(n_orig: Index, m_eq: Index, m_ineq: Index) -> Rc<CompoundVectorSpace> {
let total_dim = n_orig + 2 * m_eq + 2 * m_ineq;
let space = CompoundVectorSpace::new(5, total_dim);
let s0 = DenseVectorSpace::new(n_orig);
space.set_comp(BLOCK_X, n_orig, {
let s = Rc::clone(&s0);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
let s_eq = DenseVectorSpace::new(m_eq);
space.set_comp(BLOCK_N_C, m_eq, {
let s = Rc::clone(&s_eq);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
space.set_comp(BLOCK_P_C, m_eq, {
let s = Rc::clone(&s_eq);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
let s_ineq = DenseVectorSpace::new(m_ineq);
space.set_comp(BLOCK_N_D, m_ineq, {
let s = Rc::clone(&s_ineq);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
space.set_comp(BLOCK_P_D, m_ineq, {
let s = Rc::clone(&s_ineq);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
space
}
fn build_z_l_space(
n_xl_orig: Index,
m_eq: Index,
m_ineq: Index,
total_dim: Index,
) -> Rc<CompoundVectorSpace> {
let space = CompoundVectorSpace::new(5, total_dim);
let s0 = DenseVectorSpace::new(n_xl_orig);
space.set_comp(0, n_xl_orig, {
let s = Rc::clone(&s0);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
let s_eq = DenseVectorSpace::new(m_eq);
space.set_comp(1, m_eq, {
let s = Rc::clone(&s_eq);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
space.set_comp(2, m_eq, {
let s = Rc::clone(&s_eq);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
let s_ineq = DenseVectorSpace::new(m_ineq);
space.set_comp(3, m_ineq, {
let s = Rc::clone(&s_ineq);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
space.set_comp(4, m_ineq, {
let s = Rc::clone(&s_ineq);
move || Box::new(DenseVector::new(Rc::clone(&s)))
});
space
}
fn divide_safe(mu: Number, x: &[Number]) -> Vec<Number> {
x.iter()
.map(|&v| {
if v.abs() < 1e-300 {
mu / 1e-300
} else {
mu / v
}
})
.collect()
}
fn downcast_dense_mut(v: &mut dyn Vector) -> &mut DenseVector {
v.as_any_mut()
.downcast_mut::<DenseVector>()
.expect("RestoIterateInitializer expected a DenseVector component")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn restoration_mu_takes_max() {
assert_eq!(restoration_mu(0.1, 0.5, 0.2), 0.5);
assert_eq!(restoration_mu(1.0, 0.5, 0.2), 1.0);
assert_eq!(restoration_mu(0.1, 0.2, 0.5), 0.5);
}
#[test]
fn solve_quadratic_zero_b_gives_2a_when_a_positive() {
assert!((solve_quadratic_elem(3.0, 0.0) - 6.0).abs() < 1e-15);
}
#[test]
fn solve_quadratic_zero_b_gives_zero_when_a_negative() {
assert!(solve_quadratic_elem(-3.0, 0.0).abs() < 1e-15);
}
#[test]
fn init_slack_pair_satisfies_quadratic_root() {
let c = 0.5;
let mu_r = 0.1;
let rho = 1e3;
let (n, _p) = init_slack_pair(c, mu_r, rho);
let half = mu_r / (2.0 * rho);
let a = half - 0.5 * c;
let b = c * half;
let resid = n * n - 2.0 * a * n - b;
assert!(resid.abs() < 1e-15, "residual = {}", resid);
}
#[test]
fn init_slack_pair_p_minus_n_equals_c() {
let c = -0.7;
let (n, p) = init_slack_pair(c, 0.05, 1e2);
assert!((p - n - c).abs() < 1e-15);
}
#[test]
fn init_slack_pair_nonnegative() {
for &c in &[-1.0, -0.1, 0.0, 0.1, 1.0, 10.0] {
let (n, p) = init_slack_pair(c, 0.1, 1e3);
assert!(n >= 0.0, "n = {} for c = {}", n, c);
assert!(p >= 0.0, "p = {} for c = {}", p, c);
}
}
#[test]
fn build_z_l_space_has_five_blocks_and_total_dim() {
let space = build_z_l_space(3, 2, 1, 3 + 2 + 2 + 1 + 1);
assert_eq!(space.n_comp_spaces(), 5);
assert_eq!(space.dim(), 9);
}
#[test]
fn divide_safe_handles_zero() {
let r = divide_safe(0.5, &[1.0, 0.0, 2.0]);
assert!((r[0] - 0.5).abs() < 1e-15);
assert!(r[1].is_finite());
assert!((r[2] - 0.25).abs() < 1e-15);
}
mod end_to_end {
use super::*;
use crate::resto_nlp::RestoIpoptNlp;
use pounce_algorithm::ipopt_cq::IpoptCalculatedQuantities;
use pounce_algorithm::ipopt_data::IpoptData;
use pounce_algorithm::kkt::aug_system_solver::{
AugSysCoeffs, AugSysRhs, AugSysSol, AugSystemSolver,
};
use pounce_common::types::Index;
use pounce_linalg::{IdentityMatrix, Matrix, SymMatrix};
use pounce_linsol::status::ESymSolverStatus;
use std::cell::RefCell;
use std::rc::Rc;
struct PanickyAugSolver;
impl AugSystemSolver for PanickyAugSolver {
fn provides_inertia(&self) -> bool {
true
}
fn number_of_neg_evals(&self) -> Index {
0
}
fn increase_quality(&mut self) -> bool {
false
}
fn last_solve_status(&self) -> ESymSolverStatus {
ESymSolverStatus::Success
}
fn solve(
&mut self,
_coeffs: &AugSysCoeffs<'_>,
_rhs: &AugSysRhs<'_>,
_sol: &mut AugSysSol<'_>,
_check_neg_evals: bool,
_num_neg_evals: Index,
) -> ESymSolverStatus {
unreachable!("RestoIterateInitializer should not call aug_solver in v0.1")
}
}
struct StubOrigNlp {
x_l: DenseVector,
x_u: DenseVector,
d_l: DenseVector,
d_u: DenseVector,
px_l: Rc<dyn Matrix>,
px_u: Rc<dyn Matrix>,
pd_l: Rc<dyn Matrix>,
pd_u: Rc<dyn Matrix>,
}
impl pounce_algorithm::ipopt_nlp::Nlp for StubOrigNlp {
fn n(&self) -> Index {
2
}
fn m_eq(&self) -> Index {
1
}
fn m_ineq(&self) -> Index {
1
}
fn eval_f(&mut self, _x: &dyn Vector) -> Number {
0.0
}
fn eval_grad_f(&mut self, _x: &dyn Vector, _g: &mut dyn Vector) {}
fn eval_c(&mut self, _x: &dyn Vector, _c: &mut dyn Vector) {}
fn eval_d(&mut self, _x: &dyn Vector, _d: &mut dyn Vector) {}
fn eval_jac_c(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
Rc::new(IdentityMatrix::new(1))
}
fn eval_jac_d(&mut self, _x: &dyn Vector) -> Rc<dyn Matrix> {
Rc::new(IdentityMatrix::new(1))
}
fn eval_h(
&mut self,
_x: &dyn Vector,
_o: Number,
_y_c: &dyn Vector,
_y_d: &dyn Vector,
) -> Rc<dyn SymMatrix> {
let s = pounce_linalg::DenseSymMatrixSpace::new(2);
Rc::new(pounce_linalg::DenseSymMatrix::new(s))
}
}
impl pounce_algorithm::ipopt_nlp::IpoptNlp for StubOrigNlp {
fn x_l(&self) -> &dyn Vector {
&self.x_l
}
fn x_u(&self) -> &dyn Vector {
&self.x_u
}
fn d_l(&self) -> &dyn Vector {
&self.d_l
}
fn d_u(&self) -> &dyn Vector {
&self.d_u
}
fn px_l(&self) -> Rc<dyn Matrix> {
self.px_l.clone()
}
fn px_u(&self) -> Rc<dyn Matrix> {
self.px_u.clone()
}
fn pd_l(&self) -> Rc<dyn Matrix> {
self.pd_l.clone()
}
fn pd_u(&self) -> Rc<dyn Matrix> {
self.pd_u.clone()
}
}
fn build_resto_nlp() -> Rc<RefCell<dyn pounce_algorithm::ipopt_nlp::IpoptNlp>> {
let xl_space = DenseVectorSpace::new(2);
let mut x_l = DenseVector::new(Rc::clone(&xl_space));
x_l.set_values(&[0.0, 0.0]);
let mut x_u = DenseVector::new(Rc::clone(&xl_space));
x_u.set_values(&[10.0, 10.0]);
let dl_space = DenseVectorSpace::new(1);
let mut d_l = DenseVector::new(Rc::clone(&dl_space));
d_l.set_values(&[-5.0]);
let mut d_u = DenseVector::new(Rc::clone(&dl_space));
d_u.set_values(&[5.0]);
let stub = StubOrigNlp {
x_l,
x_u,
d_l,
d_u,
px_l: Rc::new(IdentityMatrix::new(2)),
px_u: Rc::new(IdentityMatrix::new(2)),
pd_l: Rc::new(IdentityMatrix::new(1)),
pd_u: Rc::new(IdentityMatrix::new(1)),
};
let orig: Rc<RefCell<dyn pounce_algorithm::ipopt_nlp::IpoptNlp>> =
Rc::new(RefCell::new(stub));
let mut resto = RestoIpoptNlp::new(2, 1, 1, &[1.5, 2.5], 1e3, 1e-4);
resto.set_orig_nlp(orig);
Rc::new(RefCell::new(resto))
}
fn build_outer_snapshot() -> OuterIterateSnapshot {
let c_space = DenseVectorSpace::new(1);
let mut c_vec = DenseVector::new(Rc::clone(&c_space));
c_vec.set_values(&[2.0]);
let mut dms = DenseVector::new(Rc::clone(&c_space));
dms.set_values(&[0.5]);
let s_space = DenseVectorSpace::new(1);
let mut s = DenseVector::new(Rc::clone(&s_space));
s.set_values(&[0.0]);
let xl_space = DenseVectorSpace::new(2);
let mut z_l = DenseVector::new(Rc::clone(&xl_space));
z_l.set_values(&[5.0, 1e6]); let mut z_u = DenseVector::new(Rc::clone(&xl_space));
z_u.set_values(&[2.0, 3.0]);
let dl_space = DenseVectorSpace::new(1);
let mut v_l = DenseVector::new(Rc::clone(&dl_space));
v_l.set_values(&[7.0]);
let mut v_u = DenseVector::new(Rc::clone(&dl_space));
v_u.set_values(&[2.5e3]);
OuterIterateSnapshot {
mu: 0.01,
s: Rc::new(s),
z_l: Rc::new(z_l),
z_u: Rc::new(z_u),
v_l: Rc::new(v_l),
v_u: Rc::new(v_u),
c_vec: Rc::new(c_vec),
d_minus_s_vec: Rc::new(dms),
}
}
fn build_data_and_cq(
nlp: &Rc<RefCell<dyn pounce_algorithm::ipopt_nlp::IpoptNlp>>,
) -> (
pounce_algorithm::ipopt_data::IpoptDataHandle,
pounce_algorithm::ipopt_cq::IpoptCqHandle,
) {
let data = Rc::new(RefCell::new(IpoptData::new()));
let cq = Rc::new(RefCell::new(IpoptCalculatedQuantities::new(
Rc::clone(&data),
Rc::clone(nlp),
)));
(data, cq)
}
#[test]
fn set_initial_iterates_builds_well_shaped_iv() {
let nlp = build_resto_nlp();
let (data, cq) = build_data_and_cq(&nlp);
let mut init = RestoIterateInitializer::with_dims(2, 1, 1, vec![1.5, 2.5])
.with_outer_snapshot(build_outer_snapshot())
.with_rho(1e3);
let mut aug = PanickyAugSolver;
assert!(init.set_initial_iterates(&data, &cq, &nlp, &mut aug));
let d = data.borrow();
let curr = d.curr.as_ref().expect("curr installed");
assert!((d.curr_mu - 2.0).abs() < 1e-15, "mu_R = {}", d.curr_mu);
let xc = curr
.x
.as_any()
.downcast_ref::<CompoundVector>()
.expect("inner x must be CompoundVector");
assert_eq!(xc.n_comps(), 5);
assert_eq!(xc.comp(BLOCK_X).dim(), 2);
assert_eq!(xc.comp(BLOCK_N_C).dim(), 1);
assert_eq!(xc.comp(BLOCK_P_D).dim(), 1);
let x0 = xc
.comp(BLOCK_X)
.as_any()
.downcast_ref::<DenseVector>()
.unwrap();
assert_eq!(x0.values(), &[1.5, 2.5]);
let (n_exp, p_exp) = init_slack_pair(2.0, 2.0, 1e3);
let nc = xc
.comp(BLOCK_N_C)
.as_any()
.downcast_ref::<DenseVector>()
.unwrap()
.values()[0];
let pc = xc
.comp(BLOCK_P_C)
.as_any()
.downcast_ref::<DenseVector>()
.unwrap()
.values()[0];
assert!((nc - n_exp).abs() < 1e-15);
assert!((pc - p_exp).abs() < 1e-15);
assert_eq!(curr.s.dim(), 1);
let zl = curr.z_l.as_any().downcast_ref::<CompoundVector>().unwrap();
assert_eq!(zl.n_comps(), 5);
let zl0 = zl.comp(0).as_any().downcast_ref::<DenseVector>().unwrap();
assert_eq!(zl0.values(), &[5.0, 1e3]);
let zl1 = zl.comp(1).as_any().downcast_ref::<DenseVector>().unwrap();
assert!((zl1.values()[0] - 2.0 / nc).abs() < 1e-12);
let zu = curr.z_u.as_any().downcast_ref::<DenseVector>().unwrap();
assert_eq!(zu.values(), &[2.0, 3.0]);
let vu = curr.v_u.as_any().downcast_ref::<DenseVector>().unwrap();
assert_eq!(vu.values(), &[1e3]);
}
#[test]
fn set_initial_iterates_returns_false_without_outer_snapshot() {
let nlp = build_resto_nlp();
let (data, cq) = build_data_and_cq(&nlp);
let mut init = RestoIterateInitializer::with_dims(2, 1, 1, vec![1.5, 2.5]);
let mut aug = PanickyAugSolver;
assert!(!init.set_initial_iterates(&data, &cq, &nlp, &mut aug));
}
}
}