Skip to content

Commit

Permalink
Recover from numerical instability.
Browse files Browse the repository at this point in the history
  • Loading branch information
n3vu0r committed Mar 25, 2024
1 parent c48147f commit 91e0726
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 12 deletions.
5 changes: 5 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Version 0.4.1 (2024-03-26)

* Leverage move-to-front heuristic to prevent panic due to numerical instability.
* Let `Enclosing::contains` panic for infinite points.

# Version 0.4.0 (2024-03-24)

* Lower trait bounds on `Ball` and `Enclosing`.
Expand Down
13 changes: 8 additions & 5 deletions src/ball.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ where
{
#[inline]
fn contains(&self, point: &OPoint<T, D>) -> bool {
(point - &self.center).norm_squared() <= self.radius_squared
let norm_squared = (point - &self.center).norm_squared();
assert!(norm_squared.is_finite());
norm_squared <= self.radius_squared
}
fn with_bounds(bounds: &[OPoint<T, D>]) -> Option<Self>
where
Expand Down Expand Up @@ -65,16 +67,17 @@ where
}
});
let vector = vector.view((0, 0), (length, 1));
matrix.try_inverse().map(|matrix| {
matrix.try_inverse().and_then(|matrix| {
let vector = matrix * vector;
let mut center = OVector::<T, D>::zeros();
for point in 0..length {
center += points.column(point) * vector[point].clone();
}
Self {
let radius_squared = center.norm_squared();
radius_squared.is_finite().then(|| Self {
center: &bounds[0] + &center,
radius_squared: center.norm_squared(),
}
radius_squared,
})
})
}
}
19 changes: 12 additions & 7 deletions src/enclosing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,18 @@ where
DefaultAllocator: Allocator<T, D, D> + Allocator<OPoint<T, D>, DimNameSum<D, U1>>,
<DefaultAllocator as Allocator<OPoint<T, D>, DimNameSum<D, U1>>>::Buffer: Default,
{
maybe_grow(Self::RED_ZONE, Self::STACK_SIZE, || {
Self::enclosing_points_with_bounds(
points,
&mut OVec::<OPoint<T, D>, DimNameSum<D, U1>>::new(),
)
.expect("Empty point set")
})
assert!(!points.is_empty(), "empty point set");
for _ in 0..points.len() {
if let Some(ball) = maybe_grow(Self::RED_ZONE, Self::STACK_SIZE, || {
Self::enclosing_points_with_bounds(
points,
&mut OVec::<OPoint<T, D>, DimNameSum<D, U1>>::new(),
)
}) {
return ball;
}
}
unreachable!("numerical instability");
}
/// Returns minimum ball enclosing `points` with `bounds`.
///
Expand Down

0 comments on commit 91e0726

Please sign in to comment.