use crate::errors::QlResult;
use crate::math::array::Array;
use crate::math::matrix::Matrix;
use crate::require;
use crate::types::Real;
#[derive(Clone, Debug)]
pub struct HouseholderTransformation {
v: Array,
}
impl HouseholderTransformation {
pub fn new(v: Array) -> QlResult<Self> {
validate(&v)?;
Ok(Self { v })
}
pub fn apply(&self, x: &Array) -> QlResult<Array> {
validate(x)?;
require!(x.size() == self.v.size(), "Householder dimension mismatch");
let scale = max_abs(x);
if scale == 0.0 {
return Ok(x.clone());
}
let scaled: Array = x.iter().map(|value| value / scale).collect();
let dot = self
.v
.iter()
.zip(scaled.iter())
.map(|(v, x)| v * x)
.sum::<Real>();
require!(dot.is_finite(), "Householder inner product overflowed");
if dot == 0.0 {
return Ok(x.clone());
}
let coefficient = 2.0 * dot;
require!(
coefficient.is_finite(),
"Householder coefficient overflowed"
);
self.v
.iter()
.zip(scaled.iter())
.map(|(v, x)| {
let value = (x - coefficient * v) * scale;
require!(value.is_finite(), "Householder output overflowed");
Ok(value)
})
.collect()
}
pub fn matrix(&self) -> QlResult<Matrix> {
let n = self.v.size();
require!(
n.checked_mul(n)
.is_some_and(|len| len <= isize::MAX as usize / size_of::<Real>()),
"Householder matrix is too large"
);
let scale = max_abs(&self.v);
let mut matrix = Matrix::with_size(n, n);
if scale == 0.0 {
for i in 0..n {
matrix[(i, i)] = 1.0;
}
return Ok(matrix);
}
let scaled: Array = self.v.iter().map(|v| v / scale).collect();
let norm_squared = scaled.dot(&scaled);
for i in 0..n {
for j in 0..=i {
let value =
if i == j { 1.0 } else { 0.0 } - 2.0 * scaled[i] * scaled[j] / norm_squared;
matrix[(i, j)] = value;
matrix[(j, i)] = value;
}
}
Ok(matrix)
}
}
#[derive(Clone, Debug)]
pub struct HouseholderReflection {
e: Array,
}
impl HouseholderReflection {
pub fn new(e: Array) -> QlResult<Self> {
validate(&e)?;
let is_unit = (norm(&e) - 1.0).abs() <= 32.0 * Real::EPSILON;
require!(is_unit, "Householder direction must have unit norm");
Ok(Self { e })
}
pub fn reflection_vector(&self, a: &Array) -> QlResult<Array> {
validate(a)?;
require!(a.size() == self.e.size(), "Householder dimension mismatch");
let scale = max_abs(a);
require!(scale != 0.0, "vector of length zero given");
let a: Array = a.iter().map(|value| value / scale).collect();
let dot = a.dot(&self.e);
let a1 = &self.e * dot;
let a2 = &a - &a1;
let ratio = norm(&a2) / dot.abs();
let eps = ratio * ratio;
let vector = if eps < Real::EPSILON * Real::EPSILON {
Array::with_size(a.size())
} else if eps < 1e-4 {
let eps2 = eps * eps;
let eps3 = eps * eps2;
let eps4 = eps2 * eps2;
let numerator =
&a2 - &(&a1 * (eps / 2.0 - eps2 / 8.0 + eps3 / 16.0 - 5.0 / 128.0 * eps4));
let denominator = dot * (eps + eps2 / 4.0 - eps3 / 8.0 + 5.0 / 64.0 * eps4).sqrt();
&numerator / denominator
} else {
let c = &a - &(&self.e * norm(&a));
&c / norm(&c)
};
require!(
vector.iter().all(|value| value.is_finite()),
"Householder reflection vector is nonfinite"
);
Ok(vector)
}
pub fn apply(&self, a: &Array) -> QlResult<Array> {
HouseholderTransformation::new(self.reflection_vector(a)?)?.apply(a)
}
}
pub fn householder_transformation(e: Array, target: &Array) -> QlResult<Matrix> {
HouseholderTransformation::new(HouseholderReflection::new(e)?.reflection_vector(target)?)?
.matrix()
}
fn validate(values: &Array) -> QlResult<()> {
require!(!values.is_empty(), "Householder vector must not be empty");
require!(
values.iter().all(|value| value.is_finite()),
"Householder vector must be finite"
);
Ok(())
}
fn max_abs(values: &Array) -> Real {
values
.iter()
.fold(0.0, |scale, value| scale.max(value.abs()))
}
fn norm(values: &Array) -> Real {
values.iter().fold(0.0, |norm, value| norm.hypot(*value))
}