pub struct Vector<const D: usize> { /* private fields */ }Expand description
Finite fixed-size vector of length D, stored inline.
Public construction rejects NaN and infinity through try_new,
and the storage field is private, so a Vector value carries the invariant
that every stored entry is finite. Algorithms therefore do not re-scan stored
entries at every use; user-visible non-finite errors come from construction
boundaries or from values computed during arithmetic, such as overflowed
accumulators.
Direct field construction is intentionally unavailable to downstream callers:
use la_stack::Vector;
let _ = Vector::<2> {
data: [1.0, f64::NAN],
};Implementations§
Source§impl<const D: usize> Vector<D>
impl<const D: usize> Vector<D>
Sourcepub const fn try_new(data: [f64; D]) -> Result<Self, LaError>
pub const fn try_new(data: [f64; D]) -> Result<Self, LaError>
Try to create a finite vector from a backing array.
This is the public raw-storage boundary for vectors. Successful
construction makes the returned Vector a finite-storage proof.
§Examples
use la_stack::prelude::*;
let v = Vector::<3>::try_new([1.0, 2.0, 3.0])?;
assert_eq!(v.into_array(), [1.0, 2.0, 3.0]);§Errors
Returns LaError::NonFinite with the first offending entry index when
data contains NaN or infinity.
Sourcepub const fn zero() -> Self
pub const fn zero() -> Self
All-zeros finite vector.
§Examples
use la_stack::prelude::*;
let z = Vector::<2>::zero();
assert_eq!(z.into_array(), [0.0, 0.0]);Sourcepub const fn as_array(&self) -> &[f64; D]
pub const fn as_array(&self) -> &[f64; D]
Borrow the finite backing array.
§Examples
use la_stack::prelude::*;
let v = Vector::<2>::try_new([1.0, -2.0])?;
assert_eq!(v.as_array(), &[1.0, -2.0]);Sourcepub const fn into_array(self) -> [f64; D]
pub const fn into_array(self) -> [f64; D]
Consume and return the finite backing array.
§Examples
use la_stack::prelude::*;
let v = Vector::<2>::try_new([1.0, 2.0])?;
let a = v.into_array();
assert_eq!(a, [1.0, 2.0]);Sourcepub const fn dot(&self, other: &Self) -> Result<f64, LaError>
pub const fn dot(&self, other: &Self) -> Result<f64, LaError>
Dot product.
Terms are accumulated in f64 using f64::mul_add at each index.
Intermediate rounding occurs, and this method does not provide a
certified absolute rounding bound for the returned dot product. Raw
Vector values are finite by construction, so this method only checks
whether the accumulation overflows to NaN or infinity.
§Examples
use la_stack::prelude::*;
let a = Vector::<3>::try_new([1.0, 2.0, 3.0])?;
let b = Vector::<3>::try_new([-2.0, 0.5, 4.0])?;
assert!((a.dot(&b)? - 11.0).abs() <= 1e-12);§Errors
Returns LaError::NonFinite when the accumulated dot product overflows
to NaN or infinity.
Sourcepub const fn dot_with_errbound(
&self,
other: &Self,
) -> Result<Option<ScalarWithErrorBound>, LaError>
pub const fn dot_with_errbound( &self, other: &Self, ) -> Result<Option<ScalarWithErrorBound>, LaError>
Dot product with a certified absolute roundoff bound.
The estimate uses the deterministic left-to-right recurrence
s[0] = 0 and s[i + 1] = self[i].mul_add(other[i], s[i]). When the
relative-error model is valid, the returned certificate bounds the
difference between s[D] and the exact-real expression
Σᵢ self[i] × other[i] over the stored binary64 inputs.
The bound is gamma_D × Σᵢ |self[i] × other[i]|, where
gamma_D = D u / (1 - D u) and u = 2^-53. The magnitude sum and the
published bound are rounded upward. See REFERENCES.md [9-11].
Ok(None) means no certificate is available because a nonzero product
or FMA result entered the subnormal range, the reduction dimension made
gamma_D invalid, or a proof-only magnitude/bound calculation exhausted
the finite binary64 range. It does not mean the exact dot product is
zero. Unlike a user-selected tolerance, a returned error bound describes
rounding in this specific arithmetic tree.
§Examples
use la_stack::prelude::*;
let left = Vector::<3>::try_new([1.0, 2.0, 3.0])?;
let right = Vector::<3>::try_new([4.0, 5.0, 6.0])?;
let positive = left.dot_with_errbound(&right)?.and_then(|bounded| {
if bounded.lower_bound() > 0.0 {
Some(true)
} else if bounded.upper_bound() <= 0.0 {
Some(false)
} else {
None // The enclosure cannot establish whether the result is positive.
}
});
assert_eq!(positive, Some(true));
// Finite inputs can also lack a certificate; use an exact fallback
// before deciding the sign in this case.
let tiny = Vector::<2>::try_new([1e-200, 1e-200])?;
assert_eq!(tiny.dot_with_errbound(&tiny)?, None);§Errors
Returns LaError::NonFinite with the first failing reduction index and
ArithmeticOperation::VectorDotProduct when an FMA estimate becomes
non-finite.
Sourcepub const fn dot_difference_with_errbound(
&self,
left: &Self,
right: &Self,
) -> Result<Option<ScalarWithErrorBound>, LaError>
pub const fn dot_difference_with_errbound( &self, left: &Self, right: &Self, ) -> Result<Option<ScalarWithErrorBound>, LaError>
Certified dot product with an unrounded vector difference.
This evaluates the exact-real expression
Σᵢ self[i] × (left[i] - right[i]) without first rounding
left - right into a Vector. Its deterministic arithmetic tree is
s[0] = 0
s[2i + 1] = self[i].mul_add(left[i], s[2i])
s[2i + 2] = (-self[i]).mul_add(right[i], s[2i + 1]).A returned certificate therefore includes all 2D FMA rounding events
in that tree and bounds the intended expression over the original
binary64 coordinates. When available, its absolute bound is
gamma_2D × Σᵢ (|self[i] × left[i]| + |self[i] × right[i]|), where
gamma_2D = 2D u / (1 - 2D u) and u = 2^-53; every magnitude and the
final bound are rounded upward. Its
lower_bound and
upper_bound can certify a sign or
separation from a caller’s threshold. An overlapping endpoint range
remains inconclusive and should trigger the caller’s exact fallback.
Ok(None) has the same proof-unavailable meaning as in
dot_with_errbound, including gradual
underflow and proof-only range exhaustion.
§Examples
use la_stack::prelude::*;
let axis = Vector::<2>::try_new([2.0, -1.0])?;
let left = Vector::<2>::try_new([4.0, 1.0])?;
let right = Vector::<2>::try_new([1.0, 3.0])?;
let separated = axis.dot_difference_with_errbound(&left, &right)?.and_then(|bounded| {
if bounded.lower_bound() > 1.0 {
Some(true)
} else if bounded.upper_bound() <= 1.0 {
Some(false)
} else {
None // An exact fallback is needed to decide this threshold test.
}
});
// An unavailable certificate also remains None through and_then.
assert_eq!(separated, Some(true));§Errors
Returns LaError::NonFinite with the first failing coordinate index
and ArithmeticOperation::VectorDotDifference when either FMA for
that coordinate produces a non-finite estimate.
Sourcepub const fn norm_squared(&self) -> Result<f64, LaError>
pub const fn norm_squared(&self) -> Result<f64, LaError>
Squared Euclidean norm.
This is computed as dot(self, self), so norm_squared has the same
f64 mul_add accumulation behavior as dot.
Intermediate rounding occurs, and this method does not provide a
certified absolute rounding bound for the returned squared norm.
Vector values are finite by construction, so this method only checks
whether the accumulation overflows to NaN or infinity.
§Examples
use la_stack::prelude::*;
let v = Vector::<3>::try_new([1.0, 2.0, 3.0])?;
assert!((v.norm_squared()? - 14.0).abs() <= 1e-12);§Errors
Returns LaError::NonFinite when the accumulated norm overflows to NaN
or infinity.
Sourcepub fn norm(&self) -> Result<f64, LaError>
pub fn norm(&self) -> Result<f64, LaError>
Overflow- and underflow-safe Euclidean norm.
This computes sqrt(Σᵢ self[i]²) with a deterministic left-to-right
scaled sum-of-squares recurrence. Each non-zero magnitude is divided by
the largest magnitude seen so far before it is squared, so intermediate
squares cannot overflow and an all-subnormal vector is scaled into the
normal range. See REFERENCES.md [15].
The divisions, fused multiply-adds, square root, and final rescaling are
rounded in binary64. This method does not claim correct rounding or
provide a certified absolute error bound. Because Vector entries are
finite by construction, it returns a finite non-negative result unless
the exact Euclidean norm rounds outside the finite binary64 range.
Near that boundary, a fixed-size stack accumulator sums the coordinate
squares exactly and compares squared rounding midpoints. This fallback
prevents accumulated roundoff from causing or hiding overflow and does
not require the exact feature.
Unlike norm_squared, this method does not require the
squared norm to be representable. For example, the norm of
[1.0e200, 1.0e200] is finite even though its squared norm is not.
§Examples
use la_stack::prelude::*;
let ordinary = Vector::<2>::try_new([3.0, 4.0])?;
assert_eq!(ordinary.norm()?, 5.0);
assert_eq!(ordinary.norm_squared()?, 25.0);
let large = Vector::<2>::try_new([1.0e200, 1.0e200])?;
assert!(large.norm()?.is_finite());
assert_eq!(
large.norm_squared(),
Err(LaError::non_finite_computation_step(ArithmeticOperation::VectorSquaredNorm, 0)),
);§Errors
Returns LaError::NonFinite with
ArithmeticOperation::VectorNorm when the exact Euclidean norm rounds
to infinity under round-to-nearest, ties-to-even.