use crate::problem::QpProblem;
use pounce_common::{Index, Number};
#[derive(Debug, Clone)]
pub struct KktTriplet {
pub dim: usize,
pub irn: Vec<Index>,
pub jcn: Vec<Index>,
pub vals: Vec<Number>,
}
impl KktTriplet {
pub fn add_h_diagonal_shift(&mut self, n_h_rows: usize, delta: Number) {
if delta == 0.0 {
return;
}
let n_h = n_h_rows as Index;
let mut have_diag = vec![false; n_h_rows];
for k in 0..self.irn.len() {
let i = self.irn[k];
let j = self.jcn[k];
if i == j && i >= 1 && i <= n_h {
self.vals[k] += delta;
have_diag[(i - 1) as usize] = true;
}
}
for i in 0..n_h_rows {
if !have_diag[i] {
let i1 = (i + 1) as Index;
self.irn.push(i1);
self.jcn.push(i1);
self.vals.push(delta);
}
}
}
}
impl KktTriplet {
pub fn assemble_equality_only(qp: &QpProblem) -> Self {
let n = qp.n;
let m = qp.m;
let dim = n + m;
let nh = qp.h.nonzeros() as usize;
let na = qp.a.nonzeros() as usize;
let cap = nh + na;
let mut irn = Vec::with_capacity(cap);
let mut jcn = Vec::with_capacity(cap);
let mut vals = Vec::with_capacity(cap);
let h_irows = qp.h.irows();
let h_jcols = qp.h.jcols();
let h_vals = qp.h.values();
for k in 0..nh {
let i = h_irows[k];
let j = h_jcols[k];
let (lo, hi) = if i >= j { (j, i) } else { (i, j) };
irn.push(hi);
jcn.push(lo);
vals.push(h_vals[k]);
}
let a_irows = qp.a.irows();
let a_jcols = qp.a.jcols();
let a_vals = qp.a.values();
let n_i = n as Index;
for k in 0..na {
irn.push(n_i + a_irows[k]);
jcn.push(a_jcols[k]);
vals.push(a_vals[k]);
}
Self {
dim,
irn,
jcn,
vals,
}
}
}
pub fn rhs_equality_only(qp: &QpProblem) -> Vec<Number> {
let mut rhs = Vec::with_capacity(qp.n + qp.m);
rhs.extend(qp.g.iter().map(|&gi| -gi));
rhs.extend_from_slice(qp.bl);
rhs
}
pub fn is_pure_equality_no_bounds(qp: &QpProblem) -> bool {
use pounce_common::types::{NLP_LOWER_BOUND_INF, NLP_UPPER_BOUND_INF};
for (&l, &u) in qp.bl.iter().zip(qp.bu.iter()) {
if l != u {
return false;
}
}
for (&l, &u) in qp.xl.iter().zip(qp.xu.iter()) {
if l > NLP_LOWER_BOUND_INF || u < NLP_UPPER_BOUND_INF {
return false;
}
}
true
}
pub fn is_pure_box(qp: &QpProblem) -> bool {
qp.m == 0
}
pub fn is_all_equality_constraints(qp: &QpProblem) -> bool {
qp.bl.iter().zip(qp.bu.iter()).all(|(&l, &u)| l == u)
}
pub fn assemble_box_with_active(qp: &QpProblem, active_bounds: &[usize]) -> KktTriplet {
let n = qp.n;
let k = active_bounds.len();
let dim = n + k;
let nh = qp.h.nonzeros() as usize;
let mut irn = Vec::with_capacity(nh + k);
let mut jcn = Vec::with_capacity(nh + k);
let mut vals = Vec::with_capacity(nh + k);
let h_irows = qp.h.irows();
let h_jcols = qp.h.jcols();
let h_vals = qp.h.values();
for k_h in 0..nh {
let i = h_irows[k_h];
let j = h_jcols[k_h];
let (lo, hi) = if i >= j { (j, i) } else { (i, j) };
irn.push(hi);
jcn.push(lo);
vals.push(h_vals[k_h]);
}
let n_i = n as Index;
for (j, &var) in active_bounds.iter().enumerate() {
irn.push(n_i + (j as Index) + 1);
jcn.push((var as Index) + 1);
vals.push(1.0);
}
KktTriplet {
dim,
irn,
jcn,
vals,
}
}
pub fn h_times_x(h: &pounce_linalg::triplet::SymTMatrix, x: &[Number]) -> Vec<Number> {
let n = h.space().dim() as usize;
let mut out = vec![0.0; n];
let irows = h.irows();
let jcols = h.jcols();
let vals = h.values();
for k in 0..irows.len() {
let i = (irows[k] - 1) as usize;
let j = (jcols[k] - 1) as usize;
let v = vals[k];
if i == j {
out[i] += v * x[i];
} else {
out[i] += v * x[j];
out[j] += v * x[i];
}
}
out
}
pub fn a_times_x(a: &pounce_linalg::triplet::GenTMatrix, x: &[Number], m: usize) -> Vec<Number> {
let mut out = vec![0.0; m];
let irows = a.irows();
let jcols = a.jcols();
let vals = a.values();
for k in 0..irows.len() {
let i = (irows[k] - 1) as usize;
let j = (jcols[k] - 1) as usize;
out[i] += vals[k] * x[j];
}
out
}
pub fn assemble_active_set_kkt(
qp: &QpProblem,
active_cons: &[usize],
active_bounds: &[usize],
) -> KktTriplet {
let n = qp.n;
let m = qp.m;
let k_c = active_cons.len();
let k_b = active_bounds.len();
let dim = n + k_c + k_b;
let nh = qp.h.nonzeros() as usize;
let na = qp.a.nonzeros() as usize;
let cap = nh + na + k_b;
let mut irn = Vec::with_capacity(cap);
let mut jcn = Vec::with_capacity(cap);
let mut vals = Vec::with_capacity(cap);
let h_irows = qp.h.irows();
let h_jcols = qp.h.jcols();
let h_vals = qp.h.values();
for kk in 0..nh {
let i = h_irows[kk];
let j = h_jcols[kk];
let (lo, hi) = if i >= j { (j, i) } else { (i, j) };
irn.push(hi);
jcn.push(lo);
vals.push(h_vals[kk]);
}
let mut row_offset: Vec<Option<Index>> = vec![None; m];
let n_i = n as Index;
for (j, &row) in active_cons.iter().enumerate() {
row_offset[row] = Some(n_i + (j as Index) + 1);
}
let a_irows = qp.a.irows();
let a_jcols = qp.a.jcols();
let a_vals = qp.a.values();
for kk in 0..na {
let a_row = (a_irows[kk] - 1) as usize;
if let Some(saddle_row) = row_offset[a_row] {
irn.push(saddle_row);
jcn.push(a_jcols[kk]);
vals.push(a_vals[kk]);
}
}
let nm_i = (n + k_c) as Index;
for (j, &var) in active_bounds.iter().enumerate() {
irn.push(nm_i + (j as Index) + 1);
jcn.push((var as Index) + 1);
vals.push(1.0);
}
KktTriplet {
dim,
irn,
jcn,
vals,
}
}
pub fn assemble_equality_plus_bounds(qp: &QpProblem, active_bounds: &[usize]) -> KktTriplet {
let n = qp.n;
let m = qp.m;
let k = active_bounds.len();
let dim = n + m + k;
let nh = qp.h.nonzeros() as usize;
let na = qp.a.nonzeros() as usize;
let cap = nh + na + k;
let mut irn = Vec::with_capacity(cap);
let mut jcn = Vec::with_capacity(cap);
let mut vals = Vec::with_capacity(cap);
let h_irows = qp.h.irows();
let h_jcols = qp.h.jcols();
let h_vals = qp.h.values();
for kk in 0..nh {
let i = h_irows[kk];
let j = h_jcols[kk];
let (lo, hi) = if i >= j { (j, i) } else { (i, j) };
irn.push(hi);
jcn.push(lo);
vals.push(h_vals[kk]);
}
let n_i = n as Index;
let a_irows = qp.a.irows();
let a_jcols = qp.a.jcols();
let a_vals = qp.a.values();
for kk in 0..na {
irn.push(n_i + a_irows[kk]);
jcn.push(a_jcols[kk]);
vals.push(a_vals[kk]);
}
let nm_i = (n + m) as Index;
for (j, &var) in active_bounds.iter().enumerate() {
irn.push(nm_i + (j as Index) + 1);
jcn.push((var as Index) + 1);
vals.push(1.0);
}
KktTriplet {
dim,
irn,
jcn,
vals,
}
}