use pounce_common::types::{Index, Number};
use pounce_linalg::expansion_matrix::ExpansionMatrix;
use pounce_linalg::Vector;
use std::rc::Rc;
pub fn clamp_step_to_bounds(
x_curr: &[Number],
dx: &mut [Number],
px_l: &Rc<dyn pounce_linalg::Matrix>,
px_u: &Rc<dyn pounce_linalg::Matrix>,
x_l: &dyn Vector,
x_u: &dyn Vector,
eps: Number,
) -> usize {
let n_x = x_curr.len();
if dx.len() != n_x {
return 0;
}
let mut clamped = 0;
if let Some(em) = px_l.as_any().downcast_ref::<ExpansionMatrix>() {
let bounds = compressed_values(x_l);
let expanded = em.expanded_pos_indices();
for (compressed_i, &full_pos) in expanded.iter().enumerate() {
let i = full_pos as usize;
if i >= n_x {
continue;
}
let trial = x_curr[i] + dx[i];
let lo = bounds[compressed_i];
if trial < lo - eps {
dx[i] = lo - x_curr[i];
clamped += 1;
}
}
}
if let Some(em) = px_u.as_any().downcast_ref::<ExpansionMatrix>() {
let bounds = compressed_values(x_u);
let expanded = em.expanded_pos_indices();
for (compressed_i, &full_pos) in expanded.iter().enumerate() {
let i = full_pos as usize;
if i >= n_x {
continue;
}
let trial = x_curr[i] + dx[i];
let hi = bounds[compressed_i];
if trial > hi + eps {
dx[i] = hi - x_curr[i];
clamped += 1;
}
}
}
clamped
}
fn compressed_values(v: &dyn Vector) -> Vec<Number> {
use pounce_linalg::dense_vector::DenseVector;
match v.as_any().downcast_ref::<DenseVector>() {
Some(dv) => dv.values().to_vec(),
None => Vec::new(),
}
}
pub fn clamp_with_nlp(
nlp: &dyn pounce_nlp::ipopt_nlp::IpoptNlp,
x_curr: &[Number],
dx: &mut [Number],
eps: Number,
) -> usize {
let px_l = nlp.px_l();
let px_u = nlp.px_u();
let x_l = nlp.x_l();
let x_u = nlp.x_u();
clamp_step_to_bounds(x_curr, dx, &px_l, &px_u, x_l, x_u, eps)
}
#[doc(hidden)]
pub fn _index_to_usize(i: Index) -> usize {
i as usize
}
#[cfg(test)]
mod tests {
use super::*;
use pounce_linalg::dense_vector::{DenseVector, DenseVectorSpace};
use pounce_linalg::expansion_matrix::{ExpansionMatrix, ExpansionMatrixSpace};
fn make_dv(values: &[Number]) -> DenseVector {
let space = DenseVectorSpace::new(values.len() as Index);
let mut dv = DenseVector::new(space);
dv.values_mut().copy_from_slice(values);
dv
}
#[test]
fn clamp_lowers_violating_step() {
let n_x = 3;
let x_curr = [0.1, 0.5, 0.6];
let mut dx = [-0.3, 0.0, -0.5]; let px_l_space = ExpansionMatrixSpace::new(n_x as Index, 2, &[0, 2], 0);
let px_l: Rc<dyn pounce_linalg::Matrix> = Rc::new(ExpansionMatrix::new(px_l_space));
let px_u_space = ExpansionMatrixSpace::new(n_x as Index, 0, &[], 0);
let px_u: Rc<dyn pounce_linalg::Matrix> = Rc::new(ExpansionMatrix::new(px_u_space));
let x_l = make_dv(&[0.0, 0.5]);
let x_u = make_dv(&[]);
let n = clamp_step_to_bounds(&x_curr, &mut dx, &px_l, &px_u, &x_l, &x_u, 1e-9);
assert_eq!(n, 2);
assert!((dx[0] - (-0.1)).abs() < 1e-12, "dx[0] = {}", dx[0]);
assert!((dx[1] - 0.0).abs() < 1e-12);
assert!((dx[2] - (-0.1)).abs() < 1e-12, "dx[2] = {}", dx[2]);
}
#[test]
fn clamp_uppers_violating_step() {
let n_x = 2;
let x_curr = [0.9, 1.0];
let mut dx = [0.5, 0.0]; let px_l_space = ExpansionMatrixSpace::new(n_x as Index, 0, &[], 0);
let px_l: Rc<dyn pounce_linalg::Matrix> = Rc::new(ExpansionMatrix::new(px_l_space));
let px_u_space = ExpansionMatrixSpace::new(n_x as Index, 1, &[0], 0);
let px_u: Rc<dyn pounce_linalg::Matrix> = Rc::new(ExpansionMatrix::new(px_u_space));
let x_l = make_dv(&[]);
let x_u = make_dv(&[1.0]);
let n = clamp_step_to_bounds(&x_curr, &mut dx, &px_l, &px_u, &x_l, &x_u, 1e-9);
assert_eq!(n, 1);
assert!((dx[0] - 0.1).abs() < 1e-12, "dx[0] = {}", dx[0]);
assert!((dx[1] - 0.0).abs() < 1e-12);
}
#[test]
fn clamp_is_noop_when_step_is_feasible() {
let n_x = 2;
let x_curr = [0.5, 0.5];
let mut dx = [0.1, -0.1]; let px_l_space = ExpansionMatrixSpace::new(n_x as Index, 2, &[0, 1], 0);
let px_l: Rc<dyn pounce_linalg::Matrix> = Rc::new(ExpansionMatrix::new(px_l_space));
let px_u_space = ExpansionMatrixSpace::new(n_x as Index, 2, &[0, 1], 0);
let px_u: Rc<dyn pounce_linalg::Matrix> = Rc::new(ExpansionMatrix::new(px_u_space));
let x_l = make_dv(&[0.0, 0.0]);
let x_u = make_dv(&[1.0, 1.0]);
let n = clamp_step_to_bounds(&x_curr, &mut dx, &px_l, &px_u, &x_l, &x_u, 1e-9);
assert_eq!(n, 0);
assert!((dx[0] - 0.1).abs() < 1e-12);
assert!((dx[1] - (-0.1)).abs() < 1e-12);
}
#[test]
fn clamp_respects_epsilon_tolerance() {
let n_x = 1;
let x_curr = [0.0];
let mut dx = [-5e-4];
let px_l_space = ExpansionMatrixSpace::new(n_x as Index, 1, &[0], 0);
let px_l: Rc<dyn pounce_linalg::Matrix> = Rc::new(ExpansionMatrix::new(px_l_space));
let px_u_space = ExpansionMatrixSpace::new(n_x as Index, 0, &[], 0);
let px_u: Rc<dyn pounce_linalg::Matrix> = Rc::new(ExpansionMatrix::new(px_u_space));
let x_l = make_dv(&[0.0]);
let x_u = make_dv(&[]);
let n = clamp_step_to_bounds(&x_curr, &mut dx, &px_l, &px_u, &x_l, &x_u, 1e-3);
assert_eq!(n, 0);
assert!((dx[0] - (-5e-4)).abs() < 1e-12);
}
}