pub(crate) fn should_prune(node_lower_bound: f64, incumbent: Option<f64>, gap_tol: f64) -> bool {
match incumbent {
None => false,
Some(inc) => within_gap(inc, node_lower_bound, gap_tol),
}
}
pub(crate) fn within_gap(incumbent: f64, lower_bound: f64, gap_tol: f64) -> bool {
let scale = 1.0_f64.max(incumbent.abs());
(incumbent - lower_bound) <= gap_tol * scale
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_prune_without_incumbent() {
assert!(!should_prune(-100.0, None, 1e-3));
assert!(!should_prune(0.0, None, 1e-3));
}
#[test]
fn prune_when_lower_bound_within_gap_of_incumbent() {
assert!(should_prune(-5.001, Some(-5.0), 1e-3));
}
#[test]
fn no_prune_when_lower_bound_below_incumbent_minus_gap() {
assert!(!should_prune(-10.0, Some(-5.0), 1e-3));
}
#[test]
fn gap_uses_relative_scale_for_large_incumbent() {
assert!(should_prune(1e6 - 100.0, Some(1e6), 1e-3));
assert!(!should_prune(10.0 - 100.0, Some(10.0), 1e-3));
}
#[test]
fn gap_clamps_to_unit_scale_near_zero_incumbent() {
assert!(should_prune(0.0, Some(0.001), 1e-3));
assert!(!should_prune(-0.5, Some(0.001), 1e-3));
}
}