use crate::options::SolverOptions;
pub struct WarmStartInitializer;
impl WarmStartInitializer {
#[allow(clippy::too_many_arguments)]
pub fn initialize(
x: &mut [f64],
z_l: &mut [f64],
z_u: &mut [f64],
x_l: &[f64],
x_u: &[f64],
options: &SolverOptions,
) -> f64 {
let n = x.len();
let push = options.warm_start_bound_push;
let frac = options.warm_start_bound_frac;
let mult_push = options.warm_start_mult_bound_push;
for i in 0..n {
z_l[i] = z_l[i].max(mult_push);
z_u[i] = z_u[i].max(mult_push);
}
let mut sum_compl = 0.0;
let mut count = 0;
for i in 0..n {
if x_l[i].is_finite() {
let slack = (x[i] - x_l[i]).max(1e-20);
sum_compl += slack * z_l[i];
count += 1;
}
if x_u[i].is_finite() {
let slack = (x_u[i] - x[i]).max(1e-20);
sum_compl += slack * z_u[i];
count += 1;
}
}
for i in 0..n {
if x_l[i].is_finite() {
let bound_dist = if x_u[i].is_finite() {
push.min(frac * (x_u[i] - x_l[i]))
} else {
push
};
x[i] = x[i].max(x_l[i] + bound_dist);
}
if x_u[i].is_finite() {
let bound_dist = if x_l[i].is_finite() {
push.min(frac * (x_u[i] - x_l[i]))
} else {
push
};
x[i] = x[i].min(x_u[i] - bound_dist);
}
}
if count > 0 {
(sum_compl / count as f64).max(options.mu_min)
} else {
options.mu_init
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_warmstart_no_bounds() {
let mut x = vec![1.0, 2.0];
let mut z_l = vec![0.5, 0.5];
let mut z_u = vec![0.5, 0.5];
let x_l = vec![f64::NEG_INFINITY; 2];
let x_u = vec![f64::INFINITY; 2];
let opts = SolverOptions::default();
let mu = WarmStartInitializer::initialize(&mut x, &mut z_l, &mut z_u, &x_l, &x_u, &opts);
assert!((x[0] - 1.0).abs() < 1e-15);
assert!((x[1] - 2.0).abs() < 1e-15);
assert!(z_l[0] >= opts.warm_start_mult_bound_push);
assert!(z_u[0] >= opts.warm_start_mult_bound_push);
assert!((mu - opts.mu_init).abs() < 1e-12);
}
#[test]
fn test_warmstart_lower_bound_push() {
let mut x = vec![0.0]; let mut z_l = vec![1.0];
let mut z_u = vec![0.0];
let x_l = vec![0.0];
let x_u = vec![f64::INFINITY];
let opts = SolverOptions::default();
WarmStartInitializer::initialize(&mut x, &mut z_l, &mut z_u, &x_l, &x_u, &opts);
assert!(x[0] > x_l[0], "x should be pushed from lower bound");
assert!(x[0] >= x_l[0] + opts.warm_start_bound_push);
}
#[test]
fn test_warmstart_both_bounds_push() {
let mut x = vec![0.0]; let mut z_l = vec![1.0];
let mut z_u = vec![1.0];
let x_l = vec![0.0];
let x_u = vec![10.0];
let opts = SolverOptions::default();
WarmStartInitializer::initialize(&mut x, &mut z_l, &mut z_u, &x_l, &x_u, &opts);
assert!(x[0] > x_l[0], "x pushed from lower bound");
assert!(x[0] < x_u[0], "x stays below upper bound");
}
#[test]
fn test_warmstart_narrow_bounds() {
let mut x = vec![0.0];
let mut z_l = vec![1.0];
let mut z_u = vec![1.0];
let x_l = vec![0.0];
let x_u = vec![0.001];
let opts = SolverOptions::default();
WarmStartInitializer::initialize(&mut x, &mut z_l, &mut z_u, &x_l, &x_u, &opts);
assert!(x[0] > x_l[0]);
assert!(x[0] < x_u[0]);
}
#[test]
fn test_warmstart_multiplier_floor() {
let mut x = vec![5.0];
let mut z_l = vec![-1.0]; let mut z_u = vec![-2.0]; let x_l = vec![0.0];
let x_u = vec![10.0];
let opts = SolverOptions::default();
WarmStartInitializer::initialize(&mut x, &mut z_l, &mut z_u, &x_l, &x_u, &opts);
assert!(z_l[0] >= opts.warm_start_mult_bound_push,
"z_l should be floored, got {}", z_l[0]);
assert!(z_u[0] >= opts.warm_start_mult_bound_push,
"z_u should be floored, got {}", z_u[0]);
}
#[test]
fn test_warmstart_mu_from_complementarity() {
let mut x = vec![1.0, 3.0];
let mut z_l = vec![2.0, 1.0];
let mut z_u = vec![0.5, 0.5];
let x_l = vec![0.0, 0.0];
let x_u = vec![5.0, 5.0];
let opts = SolverOptions::default();
let mu = WarmStartInitializer::initialize(&mut x, &mut z_l, &mut z_u, &x_l, &x_u, &opts);
assert!(mu > 0.0, "mu should be positive");
assert!(mu >= opts.mu_min, "mu should be at least mu_min");
}
}