Householder utilities¶
These additive Rust-only utilities do not replace QR, SVD or existing matrix decompositions. No binding or existing default changes are included.
use libitofin::math::array::Array;
use libitofin::math::matrixutilities::{HouseholderReflection, householder_transformation};
let direction = Array::from([1.0, 0.0, 0.0]);
let target = Array::from([3.0, 4.0, 0.0]);
let reflection = HouseholderReflection::new(direction.clone())?;
let reflected = reflection.apply(&target)?;
let matrix = householder_transformation(direction, &target)?;
# Ok::<(), libitofin::errors::QlError>(())
Two distinct transformation laws¶
| API | Law | Contract |
|---|---|---|
HouseholderTransformation::new(v) |
Stores v unchanged |
Finite, nonempty; zero is allowed |
apply(x) |
x - 2 (v · x) v |
Does not normalize v |
matrix() |
I - 2 y yᵀ, with y = v / ||v|| |
Normalizes nonzero v |
HouseholderReflection::new(e) |
Stores direction e unchanged |
Unit norm within 32 f64::EPSILON |
reflection_vector(a) |
QuantLib's three-branch formula | Nonzero a, matching dimension |
householder_transformation(e, a) |
Matrix from the reflection vector | Same checked inputs |
The distinction between apply and matrix is intentional upstream behavior.
For example, with v = [2, 0] and x = [3, 4], application gives [-21, 4]
whereas the normalized matrix gives [-3, 4]. Do not describe arbitrary
non-unit application as an orthogonal reflection.
Reflection branch conventions¶
- For an ordinary angle, the reflection vector is the normalized
a - ||a|| e, mapping toward the positive direction. - For a squared tangent below
1e-4, the original fourth-order expression avoids cancellation. A negative projection remains negative in this branch. - Below
f64::EPSILON², the reflection vector is zero and application is identity. Parallel and antiparallel vectors therefore remain unchanged. - Zero reflection vectors return an identity matrix in the checked Rust API. The original C++ explicit matrix instead divides by zero and produces NaNs. This defined extension also makes the convenience helper usable for parallel vectors. Zero target vectors remain an error.
Numerical and error boundaries¶
- All constructors and calculations return
QlResultfor invalid input. - Nonfinite components, empty vectors and incompatible dimensions are rejected.
- Input rescaling and
hypotnorms avoid raw squared-norm overflow/underflow. Finite large and tiny vectors are supported without requiring their physical norm to fit inf64. - Final unrepresentable values and nonfinite application coefficients are rejected. A non-unit transformation can fail on intermediate overflow even when exact arithmetic could cancel; arbitrary-precision arithmetic is not promised.
- Ordinary binary64 rounding and underflow remain. Mixed-scale components can disappear during rescaling, and components below the tiny-angle threshold are intentionally left unchanged. No bit-for-bit promise applies to rescaled computations.
- Matrix dimensions are checked for addressable size. Allocation exhaustion is
not converted into a numerical error, consistent with existing
MatrixAPIs.
Oracle provenance¶
The fixture generator compiles the original HouseholderReflection and
HouseholderTransformation implementation at QuantLib source revision
9863b578af0caa4cecabf697196533e84a8308b6. It does not reimplement the formulas
as its oracle. The separately installed QuantLib 1.43 Python wheel does not
expose these classes. Eleven original compiled cases cover all branches,
projection signs, parallel/orthogonal targets and a non-axis unit direction.
Additional Rust tests cover symmetry, orthogonality, involution, non-unit laws,
zero-vector matrix policy, large/tiny scales and checked failures. These tests
are not claims of a decomposition migration or performance improvement.