use crate::core::{Error, Matrix, Vector};
#[derive(Debug, Clone, PartialEq)]
pub struct LinearResidual {
matrix: Matrix,
target: Vector,
}
impl LinearResidual {
pub fn new(matrix: Matrix, target: Vector) -> Result<Self, Error> {
if matrix.nrows() != target.len() {
return Err(Error::DimensionMismatch {
message: format!(
"residual matrix row count ({}) must match target length ({})",
matrix.nrows(),
target.len()
),
});
}
Ok(Self { matrix, target })
}
pub fn matrix(&self) -> &Matrix {
&self.matrix
}
pub fn target(&self) -> &[f64] {
&self.target
}
pub fn x_dim(&self) -> usize {
self.matrix.ncols()
}
pub fn residual_dim(&self) -> usize {
self.matrix.nrows()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_matrix_2x3() -> Matrix {
Matrix::from_row_major(2, 3, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]).unwrap()
}
#[test]
fn valid_construction() {
let r = LinearResidual::new(make_matrix_2x3(), vec![10.0, 20.0]).unwrap();
assert_eq!(r.x_dim(), 3);
assert_eq!(r.residual_dim(), 2);
assert_eq!(r.target(), &[10.0, 20.0]);
}
#[test]
fn dimension_mismatch() {
let result = LinearResidual::new(make_matrix_2x3(), vec![1.0, 2.0, 3.0]);
match result.unwrap_err() {
Error::DimensionMismatch { message } => assert!(message.contains("residual")),
other => panic!("unexpected: {:?}", other),
}
}
#[test]
fn single_row() {
let m = Matrix::from_row_major(1, 4, vec![1.0, 0.0, 0.0, 1.0]).unwrap();
let r = LinearResidual::new(m, vec![5.0]).unwrap();
assert_eq!(r.x_dim(), 4);
assert_eq!(r.residual_dim(), 1);
}
#[test]
fn accessors() {
let m = make_matrix_2x3();
let r = LinearResidual::new(m.clone(), vec![1.0, 2.0]).unwrap();
assert_eq!(r.matrix(), &m);
assert_eq!(r.target(), &[1.0, 2.0]);
}
}