use super::VersorAction;
#[derive(Debug, Clone, PartialEq)]
pub struct HouseholderVersor {
pub v: Vec<f32>,
pub tau: f32,
}
impl HouseholderVersor {
pub fn new(v: Vec<f32>) -> Self {
let mut norm_sq = 0.0f32;
for &x in &v {
norm_sq += x * x;
}
assert!(norm_sq > 1e-15, "Householder normal vector cannot have zero norm");
let tau = 2.0 / norm_sq;
Self { v, tau }
}
#[inline(always)]
pub fn dim(&self) -> usize {
self.v.len()
}
#[inline]
pub fn reflect_in_place(&self, x: &mut [f32]) {
assert_eq!(x.len(), self.v.len(), "Vector dimension mismatch");
let mut dot = 0.0f32;
for i in 0..x.len() {
dot += self.v[i] * x[i];
}
let alpha = dot * self.tau;
for i in 0..x.len() {
x[i] -= alpha * self.v[i];
}
}
}
impl VersorAction<[f32]> for HouseholderVersor {
#[inline]
fn transform_in_place(&self, target: &mut [f32]) {
self.reflect_in_place(target);
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct HouseholderChain {
pub dim: usize,
pub versors: Vec<HouseholderVersor>,
}
impl HouseholderChain {
pub fn new(dim: usize) -> Self {
Self {
dim,
versors: Vec::new(),
}
}
pub fn push(&mut self, versor: HouseholderVersor) {
assert_eq!(versor.dim(), self.dim, "Versor dimension mismatch");
self.versors.push(versor);
}
#[inline(always)]
pub fn len(&self) -> usize {
self.versors.len()
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.versors.is_empty()
}
#[inline]
pub fn apply_in_place(&self, x: &mut [f32]) {
assert_eq!(x.len(), self.dim, "Target vector dimension mismatch");
for v in &self.versors {
v.reflect_in_place(x);
}
}
pub fn to_dense_matrix(&self) -> Vec<f32> {
let n = self.dim;
let mut q = vec![0.0f32; n * n];
for i in 0..n {
q[i * n + i] = 1.0;
}
let mut col = vec![0.0f32; n];
for j in 0..n {
for i in 0..n {
col[i] = if i == j { 1.0 } else { 0.0 };
}
self.apply_in_place(&mut col);
for i in 0..n {
q[i * n + j] = col[i];
}
}
q
}
}
impl VersorAction<[f32]> for HouseholderChain {
#[inline]
fn transform_in_place(&self, target: &mut [f32]) {
self.apply_in_place(target);
}
}