--- a/src/bisection_tree/aggregator.rs Sun Apr 27 15:45:40 2025 -0500 +++ b/src/bisection_tree/aggregator.rs Sun Apr 27 15:56:43 2025 -0500 @@ -2,8 +2,7 @@ Aggregation / summarisation of information in branches of bisection trees. */ -use crate::instance::Instance; -use crate::sets::Set; +pub use crate::bounds::Bounds; use crate::types::*; /// Trait for aggregating information about a branch of a [bisection tree][super::BT]. @@ -56,144 +55,6 @@ } } -/// Upper and lower bounds on an `F`-valued function. -#[derive(Copy, Clone, Debug)] -pub struct Bounds<F>( - /// Lower bound - pub F, - /// Upper bound - pub F, -); - -impl<F: Copy> Bounds<F> { - /// Returns the lower bound - #[inline] - pub fn lower(&self) -> F { - self.0 - } - - /// Returns the upper bound - #[inline] - pub fn upper(&self) -> F { - self.1 - } -} - -impl<F: Float> Bounds<F> { - /// Returns a uniform bound. - /// - /// This is maximum over the absolute values of the upper and lower bound. - #[inline] - pub fn uniform(&self) -> F { - let &Bounds(lower, upper) = self; - lower.abs().max(upper.abs()) - } - - /// Construct a bounds, making sure `lower` bound is less than `upper` - #[inline] - pub fn corrected(lower: F, upper: F) -> Self { - if lower <= upper { - Bounds(lower, upper) - } else { - Bounds(upper, lower) - } - } - - /// Refine the lower bound - #[inline] - pub fn refine_lower(&self, lower: F) -> Self { - let &Bounds(l, u) = self; - debug_assert!(l <= u); - Bounds(l.max(lower), u.max(lower)) - } - - /// Refine the lower bound - #[inline] - pub fn refine_upper(&self, upper: F) -> Self { - let &Bounds(l, u) = self; - debug_assert!(l <= u); - Bounds(l.min(upper), u.min(upper)) - } -} - -impl<'a, F: Float> std::ops::Add<Self> for Bounds<F> { - type Output = Self; - #[inline] - fn add(self, Bounds(l2, u2): Self) -> Self::Output { - let Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - Bounds(l1 + l2, u1 + u2) - } -} - -impl<'a, F: Float> std::ops::Mul<Self> for Bounds<F> { - type Output = Self; - #[inline] - fn mul(self, Bounds(l2, u2): Self) -> Self::Output { - let Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - let a = l1 * l2; - let b = u1 * u2; - // The order may flip when negative numbers are involved, so need min/max - Bounds(a.min(b), a.max(b)) - } -} - -impl<F: Float> std::iter::Product for Bounds<F> { - #[inline] - fn product<I>(mut iter: I) -> Self - where - I: Iterator<Item = Self>, - { - match iter.next() { - None => Bounds(F::ZERO, F::ZERO), - Some(init) => iter.fold(init, |a, b| a * b), - } - } -} - -impl<F: Float> Set<F> for Bounds<F> { - fn contains<I: Instance<F>>(&self, item: I) -> bool { - let v = item.own(); - let &Bounds(l, u) = self; - debug_assert!(l <= u); - l <= v && v <= u - } -} - -impl<F: Float> Bounds<F> { - /// Calculate a common bound (glb, lub) for two bounds. - #[inline] - pub fn common(&self, &Bounds(l2, u2): &Self) -> Self { - let &Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - Bounds(l1.min(l2), u1.max(u2)) - } - - /// Indicates whether `Self` is a superset of the argument bound. - #[inline] - pub fn superset(&self, &Bounds(l2, u2): &Self) -> bool { - let &Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - l1 <= l2 && u2 <= u1 - } - - /// Returns the greatest bound contained by both argument bounds, if one exists. - #[inline] - pub fn glb(&self, &Bounds(l2, u2): &Self) -> Option<Self> { - let &Bounds(l1, u1) = self; - debug_assert!(l1 <= u1 && l2 <= u2); - let l = l1.max(l2); - let u = u1.min(u2); - debug_assert!(l <= u); - if l < u { - Some(Bounds(l, u)) - } else { - None - } - } -} - impl<F: Float> Aggregator for Bounds<F> { #[inline] fn aggregate<I>(&mut self, aggregates: I)