Skip to content

Commit 4bed271

Browse files
isPANNclaude
andauthored
Fix #773: Add bf_vs_ilp tests for all ILP reduction rules (#778)
* Fix #773: Add bf_vs_ilp tests for all 85 ILP reduction rules Every *_ilp.rs test file now has a test_*_to_ilp_bf_vs_ilp function that solves the source problem with BruteForce, solves the ILP reduction with ILPSolver, extracts the solution back, and asserts the two objective values match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Simplify: extract assert_bf_vs_ilp helper, remove boilerplate Extract shared `assert_bf_vs_ilp` helper into `test_helpers.rs` and refactor 44 bf_vs_ilp tests to use it, eliminating ~216 lines of duplicated solve-compare boilerplate. Clean up unused Solver imports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove duplicate BF-vs-ILP checks from closed_loop tests The bf_vs_ilp tests now own the BF-vs-ILP value comparison. Simplify MVC/MIS closed_loop tests to only assert structural properties (ilp_size, is_valid). Delete OLA optimization test that was fully subsumed by bf_vs_ilp. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f29cfb1 commit 4bed271

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+411
-30
lines changed

src/rules/test_helpers.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,23 @@ pub(crate) fn assert_satisfaction_round_trip_from_satisfaction_target<R>(
193193
);
194194
}
195195

196+
#[cfg(feature = "ilp-solver")]
197+
pub(crate) fn assert_bf_vs_ilp<R>(source: &R::Source, reduction: &R)
198+
where
199+
R: ReductionResult,
200+
R::Source: Problem + 'static,
201+
R::Target: 'static,
202+
<R::Source as Problem>::Value: Aggregate + std::fmt::Debug + PartialEq,
203+
{
204+
use crate::solvers::{ILPSolver, Solver};
205+
let bf_value = BruteForce::new().solve(source);
206+
let ilp_solution = ILPSolver::new()
207+
.solve_dyn(reduction.target_problem())
208+
.expect("ILP should be solvable");
209+
let extracted = reduction.extract_solution(&ilp_solution);
210+
assert_eq!(source.evaluate(&extracted), bf_value);
211+
}
212+
196213
pub(crate) fn solve_optimization_problem<P>(problem: &P) -> Option<Vec<usize>>
197214
where
198215
P: Problem + 'static,

src/unit_tests/rules/acyclicpartition_ilp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,10 @@ fn test_infeasible_instance() {
7878
let solver = ILPSolver::new();
7979
assert!(solver.solve(ilp).is_none());
8080
}
81+
82+
#[test]
83+
fn test_acyclicpartition_to_ilp_bf_vs_ilp() {
84+
let source = small_instance();
85+
let reduction: ReductionAcyclicPartitionToILP = ReduceTo::<ILP<i32>>::reduce_to(&source);
86+
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
87+
}

src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,10 @@ fn test_extract_solution_identity() {
5858
assert_eq!(extracted, vec![1, 1, 0, 1, 1, 0]);
5959
assert!(source.evaluate(&extracted).0);
6060
}
61+
62+
#[test]
63+
fn test_balancedcompletebipartitesubgraph_to_ilp_bf_vs_ilp() {
64+
let source = small_instance();
65+
let reduction: ReductionBCBSToILP = ReduceTo::<ILP<bool>>::reduce_to(&source);
66+
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
67+
}

src/unit_tests/rules/bicliquecover_ilp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,10 @@ fn test_single_edge() {
6161
"single edge biclique cover",
6262
);
6363
}
64+
65+
#[test]
66+
fn test_bicliquecover_to_ilp_bf_vs_ilp() {
67+
let source = small_instance();
68+
let reduction: ReductionBicliqueCoverToILP = ReduceTo::<ILP<bool>>::reduce_to(&source);
69+
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
70+
}

src/unit_tests/rules/biconnectivityaugmentation_ilp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,10 @@ fn test_already_biconnected() {
7777
let extracted = reduction.extract_solution(&ilp_sol);
7878
assert!(source.evaluate(&extracted).0);
7979
}
80+
81+
#[test]
82+
fn test_biconnectivityaugmentation_to_ilp_bf_vs_ilp() {
83+
let source = small_instance();
84+
let reduction: ReductionBiconnAugToILP = ReduceTo::<ILP<i32>>::reduce_to(&source);
85+
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
86+
}

src/unit_tests/rules/binpacking_ilp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,10 @@ fn test_solve_reduced() {
141141
assert!(problem.evaluate(&solution).is_valid());
142142
assert_eq!(problem.evaluate(&solution), Min(Some(3)));
143143
}
144+
145+
#[test]
146+
fn test_binpacking_to_ilp_bf_vs_ilp() {
147+
let problem = BinPacking::new(vec![3, 3, 2], 5);
148+
let reduction: ReductionBPToILP = ReduceTo::<ILP<bool>>::reduce_to(&problem);
149+
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
150+
}

src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,10 @@ fn test_no_hamiltonian_cycle_infeasible() {
9696
"Path graph should have no Hamiltonian cycle"
9797
);
9898
}
99+
100+
#[test]
101+
fn test_bottlenecktravelingsalesman_to_ilp_bf_vs_ilp() {
102+
let problem = k4_btsp();
103+
let reduction: ReductionBTSPToILP = ReduceTo::<ILP<i32>>::reduce_to(&problem);
104+
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
105+
}

src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,10 @@ fn test_infeasible_instance() {
8383
let solver = ILPSolver::new();
8484
assert!(solver.solve(ilp).is_none());
8585
}
86+
87+
#[test]
88+
fn test_boundedcomponentspanningforest_to_ilp_bf_vs_ilp() {
89+
let source = small_instance();
90+
let reduction: ReductionBCSFToILP = ReduceTo::<ILP<i32>>::reduce_to(&source);
91+
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
92+
}

src/unit_tests/rules/capacityassignment_ilp.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,15 @@ fn test_capacityassignment_to_ilp_trivial() {
101101
let extracted = reduction.extract_solution(&ilp_solution);
102102
assert!(problem.evaluate(&extracted).0.is_some());
103103
}
104+
105+
#[test]
106+
fn test_capacityassignment_to_ilp_bf_vs_ilp() {
107+
let problem = CapacityAssignment::new(
108+
vec![1, 2, 3],
109+
vec![vec![1, 3, 6], vec![2, 4, 7], vec![1, 2, 5]],
110+
vec![vec![8, 4, 1], vec![7, 3, 1], vec![6, 3, 1]],
111+
12,
112+
);
113+
let reduction: ReductionCAToILP = ReduceTo::<ILP<bool>>::reduce_to(&problem);
114+
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
115+
}

src/unit_tests/rules/coloring_ilp.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,10 @@ fn test_single_edge() {
271271
assert!(problem.evaluate(&extracted));
272272
assert_ne!(extracted[0], extracted[1]);
273273
}
274+
275+
#[test]
276+
fn test_coloring_to_ilp_bf_vs_ilp() {
277+
let problem = KColoring::<K3, _>::new(SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]));
278+
let reduction = ReduceTo::<ILP<bool>>::reduce_to(&problem);
279+
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
280+
}

0 commit comments

Comments
 (0)