use crate::bound_tighten::{row_activity_pub, LinearRow, INF_BOUND};
use pounce_common::types::Number;
pub fn find_redundant_rows(
rows: &[LinearRow],
x_l: &[Number],
x_u: &[Number],
tol: Number,
) -> Vec<bool> {
rows.iter()
.map(|row| is_redundant(row, x_l, x_u, tol))
.collect()
}
fn is_redundant(row: &LinearRow, x_l: &[Number], x_u: &[Number], tol: Number) -> bool {
let act = row_activity_pub(row, x_l, x_u);
let lo_satisfied =
row.lo <= -INF_BOUND || (act.lo_neg_inf == 0 && act.lo_finite >= row.lo - tol);
let hi_satisfied =
row.hi >= INF_BOUND || (act.hi_pos_inf == 0 && act.hi_finite <= row.hi + tol);
lo_satisfied && hi_satisfied
}
#[cfg(test)]
mod tests {
use super::*;
use pounce_common::types::Index;
fn row(entries: &[(Index, Number)], lo: Number, hi: Number) -> LinearRow {
LinearRow {
entries: entries.to_vec(),
lo,
hi,
}
}
#[test]
fn double_sided_implied_by_box() {
let r = vec![row(&[(0, 1.0), (1, 1.0)], 0.0, 2.0)];
let mask = find_redundant_rows(&r, &[0.0, 0.0], &[1.0, 1.0], 1e-12);
assert_eq!(mask, vec![true]);
}
#[test]
fn equality_row_never_redundant_unless_pinned() {
let r = vec![row(&[(0, 1.0), (1, 1.0)], 1.0, 1.0)];
let mask = find_redundant_rows(&r, &[0.0, 0.0], &[1.0, 1.0], 1e-12);
assert_eq!(mask, vec![false]);
}
#[test]
fn open_upper_redundancy() {
let r = vec![row(&[(0, 1.0)], -INF_BOUND, 5.0)];
let mask = find_redundant_rows(&r, &[0.0], &[1.0], 1e-12);
assert_eq!(mask, vec![true]);
}
#[test]
fn unbounded_var_blocks_redundancy() {
let r = vec![row(&[(0, 1.0)], -INF_BOUND, 5.0)];
let mask = find_redundant_rows(&r, &[-INF_BOUND], &[INF_BOUND], 1e-12);
assert_eq!(mask, vec![false]);
}
}