use crate::core::{Error, Matrix, Vector};
#[derive(Debug, Clone, PartialEq)]
pub struct LinearEqualities {
matrix: Matrix,
rhs: Vector,
}
impl LinearEqualities {
pub fn new(matrix: Matrix, rhs: Vector) -> Result<Self, Error> {
if matrix.nrows() != rhs.len() {
return Err(Error::DimensionMismatch {
message: format!(
"equality matrix row count ({}) must match rhs length ({})",
matrix.nrows(),
rhs.len()
),
});
}
Ok(Self { matrix, rhs })
}
pub fn matrix(&self) -> &Matrix {
&self.matrix
}
pub fn rhs(&self) -> &[f64] {
&self.rhs
}
pub fn rows(&self) -> usize {
self.matrix.nrows()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LinearInequalities {
matrix: Matrix,
rhs: Vector,
}
impl LinearInequalities {
pub fn new(matrix: Matrix, rhs: Vector) -> Result<Self, Error> {
if matrix.nrows() != rhs.len() {
return Err(Error::DimensionMismatch {
message: format!(
"inequality matrix row count ({}) must match rhs length ({})",
matrix.nrows(),
rhs.len()
),
});
}
Ok(Self { matrix, rhs })
}
pub fn matrix(&self) -> &Matrix {
&self.matrix
}
pub fn rhs(&self) -> &[f64] {
&self.rhs
}
pub fn rows(&self) -> usize {
self.matrix.nrows()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Bounds {
lower: Vec<Option<f64>>,
upper: Vec<Option<f64>>,
}
impl Bounds {
pub fn new(lower: Vec<Option<f64>>, upper: Vec<Option<f64>>) -> Result<Self, Error> {
if lower.len() != upper.len() {
return Err(Error::DimensionMismatch {
message: format!(
"lower bound length ({}) must match upper bound length ({})",
lower.len(),
upper.len()
),
});
}
Ok(Self { lower, upper })
}
pub fn free(dim: usize) -> Self {
Self {
lower: vec![None; dim],
upper: vec![None; dim],
}
}
pub fn nonnegative(dim: usize) -> Self {
Self {
lower: vec![Some(0.0); dim],
upper: vec![None; dim],
}
}
pub fn len(&self) -> usize {
self.lower.len()
}
pub fn is_empty(&self) -> bool {
self.lower.is_empty()
}
pub fn lower(&self) -> &[Option<f64>] {
&self.lower
}
pub fn upper(&self) -> &[Option<f64>] {
&self.upper
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn equalities_valid() {
let m = Matrix::from_row_major(2, 3, vec![1.0; 6]).unwrap();
let eq = LinearEqualities::new(m, vec![1.0, 2.0]).unwrap();
assert_eq!(eq.rows(), 2);
assert_eq!(eq.rhs(), &[1.0, 2.0]);
}
#[test]
fn equalities_dimension_mismatch() {
let m = Matrix::from_row_major(2, 3, vec![1.0; 6]).unwrap();
assert!(LinearEqualities::new(m, vec![1.0]).is_err());
}
#[test]
fn equalities_accessors() {
let m = Matrix::from_row_major(1, 2, vec![3.0, 4.0]).unwrap();
let eq = LinearEqualities::new(m.clone(), vec![5.0]).unwrap();
assert_eq!(eq.matrix(), &m);
assert_eq!(eq.rhs(), &[5.0]);
assert_eq!(eq.rows(), 1);
}
#[test]
fn inequalities_valid() {
let m = Matrix::from_row_major(1, 2, vec![1.0, -1.0]).unwrap();
let ineq = LinearInequalities::new(m, vec![0.0]).unwrap();
assert_eq!(ineq.rows(), 1);
assert_eq!(ineq.rhs(), &[0.0]);
}
#[test]
fn inequalities_dimension_mismatch() {
let m = Matrix::from_row_major(2, 2, vec![1.0; 4]).unwrap();
assert!(LinearInequalities::new(m, vec![1.0]).is_err());
}
#[test]
fn inequalities_accessors() {
let m = Matrix::from_row_major(2, 2, vec![1.0, 0.0, 0.0, 1.0]).unwrap();
let ineq = LinearInequalities::new(m.clone(), vec![10.0, 20.0]).unwrap();
assert_eq!(ineq.matrix(), &m);
assert_eq!(ineq.rhs(), &[10.0, 20.0]);
assert_eq!(ineq.rows(), 2);
}
#[test]
fn bounds_free() {
let b = Bounds::free(3);
assert_eq!(b.len(), 3);
assert!(!b.is_empty());
assert!(b.lower().iter().all(|v| v.is_none()));
assert!(b.upper().iter().all(|v| v.is_none()));
}
#[test]
fn bounds_nonnegative() {
let b = Bounds::nonnegative(2);
assert_eq!(b.lower(), &[Some(0.0), Some(0.0)]);
assert!(b.upper().iter().all(|v| v.is_none()));
}
#[test]
fn bounds_custom() {
let b = Bounds::new(vec![Some(-1.0), None], vec![Some(1.0), Some(10.0)]).unwrap();
assert_eq!(b.len(), 2);
assert_eq!(b.lower(), &[Some(-1.0), None]);
assert_eq!(b.upper(), &[Some(1.0), Some(10.0)]);
}
#[test]
fn bounds_length_mismatch() {
assert!(Bounds::new(vec![None, None], vec![None]).is_err());
}
#[test]
fn bounds_empty() {
let b = Bounds::free(0);
assert!(b.is_empty());
assert_eq!(b.len(), 0);
}
}