use crate::error::{QpError, QpStatus};
use crate::working_set::WorkingSet;
use pounce_common::Number;
use pounce_linalg::triplet::{GenTMatrix, SymTMatrix};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum HessianInertia {
Psd,
Indefinite,
#[default]
Unknown,
}
pub struct QpProblem<'a> {
pub n: usize,
pub m: usize,
pub h: &'a SymTMatrix,
pub g: &'a [Number],
pub a: &'a GenTMatrix,
pub bl: &'a [Number],
pub bu: &'a [Number],
pub xl: &'a [Number],
pub xu: &'a [Number],
pub hessian_inertia: HessianInertia,
}
impl<'a> QpProblem<'a> {
pub fn validate(&self) -> Result<(), QpError> {
if self.h.space().dim() as usize != self.n {
return Err(QpError::DimensionMismatch(format!(
"H is {}×{} but n = {}",
self.h.space().dim(),
self.h.space().dim(),
self.n
)));
}
if self.g.len() != self.n {
return Err(QpError::DimensionMismatch(format!(
"g.len() = {} but n = {}",
self.g.len(),
self.n
)));
}
if self.a.space().n_rows() as usize != self.m || self.a.space().n_cols() as usize != self.n
{
return Err(QpError::DimensionMismatch(format!(
"A is {}×{} but expected {}×{}",
self.a.space().n_rows(),
self.a.space().n_cols(),
self.m,
self.n
)));
}
if self.bl.len() != self.m || self.bu.len() != self.m {
return Err(QpError::DimensionMismatch(format!(
"bl.len() = {}, bu.len() = {}, but m = {}",
self.bl.len(),
self.bu.len(),
self.m
)));
}
if self.xl.len() != self.n || self.xu.len() != self.n {
return Err(QpError::DimensionMismatch(format!(
"xl.len() = {}, xu.len() = {}, but n = {}",
self.xl.len(),
self.xu.len(),
self.n
)));
}
for (i, (&l, &u)) in self.bl.iter().zip(self.bu.iter()).enumerate() {
if l > u {
return Err(QpError::InvertedBounds(format!(
"constraint row {i}: bl = {l} > bu = {u}"
)));
}
}
for (i, (&l, &u)) in self.xl.iter().zip(self.xu.iter()).enumerate() {
if l > u {
return Err(QpError::InvertedBounds(format!(
"variable {i}: xl = {l} > xu = {u}"
)));
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct QpWarmStart {
pub x: Vec<Number>,
pub lambda_g: Vec<Number>,
pub lambda_x: Vec<Number>,
pub working: WorkingSet,
}
#[derive(Debug, Clone)]
pub struct QpSolution {
pub x: Vec<Number>,
pub lambda_g: Vec<Number>,
pub lambda_x: Vec<Number>,
pub working: WorkingSet,
pub obj: Number,
pub status: QpStatus,
pub stats: QpStats,
}
#[derive(Debug, Clone, Default)]
pub struct QpStats {
pub n_working_set_changes: u32,
pub n_refactor: u32,
pub n_schur_updates: u32,
pub used_phase1: bool,
pub time: Duration,
}