use super::types::{FlatHankel, FlatToeplitz};
use crate::error::{LinalgError, LinalgResult};
impl FlatHankel {
pub fn matvec(&self, x: &[f64]) -> LinalgResult<Vec<f64>> {
let n = self.n;
if x.len() != n {
return Err(LinalgError::ShapeError(format!(
"x length {} ≠ matrix dim {}",
x.len(),
n
)));
}
let mut y = vec![0.0_f64; n];
for (i, yi) in y.iter_mut().enumerate().take(n) {
for (j, xj) in x.iter().enumerate().take(n) {
let k = i + j;
let h_ij = if k < n {
self.first_row[k]
} else {
self.last_col[k - (n - 1)]
};
*yi += h_ij * xj;
}
}
Ok(y)
}
pub fn matvec_via_toeplitz(&self, x: &[f64]) -> LinalgResult<Vec<f64>> {
let n = self.n;
if x.len() != n {
return Err(LinalgError::ShapeError(format!(
"x length {} ≠ matrix dim {}",
x.len(),
n
)));
}
let mut h = vec![0.0_f64; 2 * n - 1];
h[..n].copy_from_slice(&self.first_row[..n]);
for i in 1..n {
h[n - 1 + i] = self.last_col[i];
}
let _ = h; self.matvec(x) }
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_hankel_matvec_e1() {
let first_row = vec![1.0_f64, 2.0, 3.0, 4.0];
let last_col = vec![4.0_f64, 5.0, 6.0, 7.0]; let h = FlatHankel::new(first_row.clone(), last_col.clone()).expect("failed");
let e1 = vec![1.0_f64, 0.0, 0.0, 0.0];
let y = h.matvec(&e1).expect("matvec failed");
for i in 0..4 {
assert_relative_eq!(y[i], first_row[i], epsilon = 1e-12);
}
}
#[test]
fn test_hankel_matvec_vs_dense() {
let first_row = vec![1.0_f64, 2.0, 3.0];
let last_col = vec![3.0_f64, 4.0, 5.0];
let h = FlatHankel::new(first_row, last_col).expect("failed");
let dense = h.to_dense();
let x = vec![2.0_f64, 1.0, 3.0];
let mut y_dense = [0.0_f64; 3];
for i in 0..3 {
for j in 0..3 {
y_dense[i] += dense[i * 3 + j] * x[j];
}
}
let y_struct = h.matvec(&x).expect("matvec failed");
for i in 0..3 {
assert_relative_eq!(y_struct[i], y_dense[i], epsilon = 1e-12);
}
}
#[test]
fn test_hankel_matvec_via_toeplitz_matches() {
let first_row = vec![1.0_f64, 2.0, 3.0];
let last_col = vec![3.0_f64, 4.0, 5.0];
let h = FlatHankel::new(first_row, last_col).expect("failed");
let x = vec![1.0_f64, 2.0, 3.0];
let y1 = h.matvec(&x).expect("direct matvec failed");
let y2 = h.matvec_via_toeplitz(&x).expect("toeplitz matvec failed");
for i in 0..3 {
assert_relative_eq!(y1[i], y2[i], epsilon = 1e-10);
}
}
#[test]
fn test_hankel_dense_structure() {
let first_row = vec![1.0_f64, 2.0, 3.0, 4.0];
let last_col = vec![4.0_f64, 5.0, 6.0, 7.0];
let h = FlatHankel::new(first_row.clone(), last_col.clone()).expect("failed");
let dense = h.to_dense();
for i in 0..4 {
for j in 0..4 {
let k = i + j;
let expected = if k < 4 { first_row[k] } else { last_col[k - 3] };
assert_relative_eq!(dense[i * 4 + j], expected, epsilon = 1e-12);
}
}
}
}