From b08f2a8035e0438c4e0b1fbb8cc54d1dae0d2177 Mon Sep 17 00:00:00 2001 From: Xiwei Pan Date: Thu, 26 Mar 2026 22:25:58 +0800 Subject: [PATCH 1/3] 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) --- src/unit_tests/rules/acyclicpartition_ilp.rs | 14 ++++++++++++- .../balancedcompletebipartitesubgraph_ilp.rs | 13 ++++++++++++ src/unit_tests/rules/bicliquecover_ilp.rs | 13 ++++++++++++ .../rules/biconnectivityaugmentation_ilp.rs | 14 ++++++++++++- src/unit_tests/rules/binpacking_ilp.rs | 14 ++++++++++++- .../rules/bottlenecktravelingsalesman_ilp.rs | 14 ++++++++++++- .../boundedcomponentspanningforest_ilp.rs | 14 ++++++++++++- .../rules/capacityassignment_ilp.rs | 19 ++++++++++++++++- src/unit_tests/rules/coloring_ilp.rs | 14 ++++++++++++- .../directedtwocommodityintegralflow_ilp.rs | 14 ++++++++++++- .../rules/disjointconnectingpaths_ilp.rs | 17 +++++++++++++++ src/unit_tests/rules/factoring_ilp.rs | 15 ++++++++++++- src/unit_tests/rules/hamiltonianpath_ilp.rs | 14 ++++++++++++- .../rules/integralflowbundles_ilp.rs | 14 ++++++++++++- .../rules/integralflowhomologousarcs_ilp.rs | 21 ++++++++++++++++++- .../rules/integralflowwithmultipliers_ilp.rs | 21 ++++++++++++++++++- .../rules/lengthboundeddisjointpaths_ilp.rs | 19 +++++++++++++++++ src/unit_tests/rules/longestcircuit_ilp.rs | 17 ++++++++++++++- .../rules/longestcommonsubsequence_ilp.rs | 12 +++++++++++ src/unit_tests/rules/longestpath_ilp.rs | 14 ++++++++++++- src/unit_tests/rules/maximumclique_ilp.rs | 18 +++++++++++++++- .../rules/maximumindependentset_ilp.rs | 16 +++++++++++++- src/unit_tests/rules/maximummatching_ilp.rs | 15 ++++++++++++- src/unit_tests/rules/maximumsetpacking_ilp.rs | 14 ++++++++++++- .../rules/minimumcutintoboundedsets_ilp.rs | 13 ++++++++++++ .../rules/minimumdominatingset_ilp.rs | 17 ++++++++++++++- .../rules/minimumfeedbackvertexset_ilp.rs | 15 ++++++++++++- .../rules/minimummultiwaycut_ilp.rs | 14 ++++++++++++- .../rules/minimumsetcovering_ilp.rs | 14 ++++++++++++- .../rules/minimumvertexcover_ilp.rs | 15 +++++++++++++ .../rules/optimallineararrangement_ilp.rs | 15 +++++++++++-- .../rules/pathconstrainednetworkflow_ilp.rs | 21 ++++++++++++++++++- .../precedenceconstrainedscheduling_ilp.rs | 14 ++++++++++++- .../rules/quadraticassignment_ilp.rs | 14 ++++++++++++- src/unit_tests/rules/ruralpostman_ilp.rs | 18 +++++++++++++++- .../schedulingwithindividualdeadlines_ilp.rs | 14 ++++++++++++- ...ingtominimizeweightedcompletiontime_ilp.rs | 14 ++++++++++++- .../rules/sequencingwithinintervals_ilp.rs | 14 ++++++++++++- src/unit_tests/rules/stackercrane_ilp.rs | 14 +++++++++++++ src/unit_tests/rules/steinertree_ilp.rs | 14 ++++++++++++- .../rules/steinertreeingraphs_ilp.rs | 18 ++++++++++++++++ .../strongconnectivityaugmentation_ilp.rs | 14 ++++++++++++- .../rules/subgraphisomorphism_ilp.rs | 16 +++++++++++++- src/unit_tests/rules/travelingsalesman_ilp.rs | 17 ++++++++++++++- .../rules/undirectedflowlowerbounds_ilp.rs | 14 ++++++++++++- .../undirectedtwocommodityintegralflow_ilp.rs | 14 ++++++++++++- 46 files changed, 665 insertions(+), 38 deletions(-) diff --git a/src/unit_tests/rules/acyclicpartition_ilp.rs b/src/unit_tests/rules/acyclicpartition_ilp.rs index 3aa3c8ccd..a0d3447c5 100644 --- a/src/unit_tests/rules/acyclicpartition_ilp.rs +++ b/src/unit_tests/rules/acyclicpartition_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::models::graph::AcyclicPartition; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -78,3 +78,15 @@ fn test_infeasible_instance() { let solver = ILPSolver::new(); assert!(solver.solve(ilp).is_none()); } + +#[test] +fn test_acyclicpartition_to_ilp_bf_vs_ilp() { + let source = small_instance(); + let reduction: ReductionAcyclicPartitionToILP = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs b/src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs index 6ba457bd6..b29207edb 100644 --- a/src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs +++ b/src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs @@ -3,6 +3,7 @@ use crate::models::algebraic::ILP; use crate::models::graph::BalancedCompleteBipartiteSubgraph; use crate::rules::test_helpers::assert_satisfaction_round_trip_from_satisfaction_target; use crate::rules::ReduceTo; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::BipartiteGraph; use crate::traits::Problem; @@ -58,3 +59,15 @@ fn test_extract_solution_identity() { assert_eq!(extracted, vec![1, 1, 0, 1, 1, 0]); assert!(source.evaluate(&extracted).0); } + +#[test] +fn test_balancedcompletebipartitesubgraph_to_ilp_bf_vs_ilp() { + let source = small_instance(); + let reduction: ReductionBCBSToILP = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/bicliquecover_ilp.rs b/src/unit_tests/rules/bicliquecover_ilp.rs index e3617056b..c39ac3cb7 100644 --- a/src/unit_tests/rules/bicliquecover_ilp.rs +++ b/src/unit_tests/rules/bicliquecover_ilp.rs @@ -3,6 +3,7 @@ use crate::models::algebraic::ILP; use crate::models::graph::BicliqueCover; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::BipartiteGraph; use crate::traits::Problem; @@ -61,3 +62,15 @@ fn test_single_edge() { "single edge biclique cover", ); } + +#[test] +fn test_bicliquecover_to_ilp_bf_vs_ilp() { + let source = small_instance(); + let reduction: ReductionBicliqueCoverToILP = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/biconnectivityaugmentation_ilp.rs b/src/unit_tests/rules/biconnectivityaugmentation_ilp.rs index 6ae7488cf..bad3c90d2 100644 --- a/src/unit_tests/rules/biconnectivityaugmentation_ilp.rs +++ b/src/unit_tests/rules/biconnectivityaugmentation_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::models::graph::BiconnectivityAugmentation; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -77,3 +77,15 @@ fn test_already_biconnected() { let extracted = reduction.extract_solution(&ilp_sol); assert!(source.evaluate(&extracted).0); } + +#[test] +fn test_biconnectivityaugmentation_to_ilp_bf_vs_ilp() { + let source = small_instance(); + let reduction: ReductionBiconnAugToILP = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/binpacking_ilp.rs b/src/unit_tests/rules/binpacking_ilp.rs index b7b63cf38..26f190dc5 100644 --- a/src/unit_tests/rules/binpacking_ilp.rs +++ b/src/unit_tests/rules/binpacking_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; use crate::types::Min; @@ -141,3 +141,15 @@ fn test_solve_reduced() { assert!(problem.evaluate(&solution).is_valid()); assert_eq!(problem.evaluate(&solution), Min(Some(3))); } + +#[test] +fn test_binpacking_to_ilp_bf_vs_ilp() { + let problem = BinPacking::new(vec![3, 3, 2], 5); + let reduction: ReductionBPToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs b/src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs index fbe2a0e46..1642bad50 100644 --- a/src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs +++ b/src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -96,3 +96,15 @@ fn test_no_hamiltonian_cycle_infeasible() { "Path graph should have no Hamiltonian cycle" ); } + +#[test] +fn test_bottlenecktravelingsalesman_to_ilp_bf_vs_ilp() { + let problem = k4_btsp(); + let reduction: ReductionBTSPToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs b/src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs index 08836b807..3594e1b14 100644 --- a/src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs +++ b/src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::models::graph::BoundedComponentSpanningForest; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -83,3 +83,15 @@ fn test_infeasible_instance() { let solver = ILPSolver::new(); assert!(solver.solve(ilp).is_none()); } + +#[test] +fn test_boundedcomponentspanningforest_to_ilp_bf_vs_ilp() { + let source = small_instance(); + let reduction: ReductionBCSFToILP = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/capacityassignment_ilp.rs b/src/unit_tests/rules/capacityassignment_ilp.rs index eae714745..7667cafc4 100644 --- a/src/unit_tests/rules/capacityassignment_ilp.rs +++ b/src/unit_tests/rules/capacityassignment_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; use crate::types::Min; @@ -101,3 +101,20 @@ fn test_capacityassignment_to_ilp_trivial() { let extracted = reduction.extract_solution(&ilp_solution); assert!(problem.evaluate(&extracted).0.is_some()); } + +#[test] +fn test_capacityassignment_to_ilp_bf_vs_ilp() { + let problem = CapacityAssignment::new( + vec![1, 2, 3], + vec![vec![1, 3, 6], vec![2, 4, 7], vec![1, 2, 5]], + vec![vec![8, 4, 1], vec![7, 3, 1], vec![6, 3, 1]], + 12, + ); + let reduction: ReductionCAToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/coloring_ilp.rs b/src/unit_tests/rules/coloring_ilp.rs index f9cbfa0b9..ed6ca8561 100644 --- a/src/unit_tests/rules/coloring_ilp.rs +++ b/src/unit_tests/rules/coloring_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; use crate::variant::{K1, K2, K3, K4}; @@ -271,3 +271,15 @@ fn test_single_edge() { assert!(problem.evaluate(&extracted)); assert_ne!(extracted[0], extracted[1]); } + +#[test] +fn test_coloring_to_ilp_bf_vs_ilp() { + let problem = KColoring::::new(SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)])); + let reduction = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs b/src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs index a813325f5..5dbe97b43 100644 --- a/src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs +++ b/src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -118,3 +118,15 @@ fn test_directedtwocommodityintegralflow_to_ilp_extract_solution() { "manually extracted solution should be valid" ); } + +#[test] +fn test_directedtwocommodityintegralflow_to_ilp_bf_vs_ilp() { + let problem = feasible_instance(); + let reduction: ReductionD2CIFToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/disjointconnectingpaths_ilp.rs b/src/unit_tests/rules/disjointconnectingpaths_ilp.rs index 645e07977..9b9081e6c 100644 --- a/src/unit_tests/rules/disjointconnectingpaths_ilp.rs +++ b/src/unit_tests/rules/disjointconnectingpaths_ilp.rs @@ -2,7 +2,9 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::test_helpers::assert_satisfaction_round_trip_from_satisfaction_target; use crate::rules::ReduceTo; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; +use crate::traits::Problem; #[test] fn test_disjointconnectingpaths_to_ilp_closed_loop() { @@ -20,3 +22,18 @@ fn test_disjointconnectingpaths_to_ilp_closed_loop() { "DisjointConnectingPaths->ILP closed loop", ); } + +#[test] +fn test_disjointconnectingpaths_to_ilp_bf_vs_ilp() { + let source = DisjointConnectingPaths::new( + SimpleGraph::new(6, vec![(0, 1), (1, 2), (3, 4), (4, 5)]), + vec![(0, 2), (3, 5)], + ); + let reduction = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/factoring_ilp.rs b/src/unit_tests/rules/factoring_ilp.rs index 06f1fd7bf..866cc876d 100644 --- a/src/unit_tests/rules/factoring_ilp.rs +++ b/src/unit_tests/rules/factoring_ilp.rs @@ -1,5 +1,6 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::traits::Problem; #[test] fn test_reduction_creates_valid_ilp() { @@ -297,3 +298,15 @@ fn test_variable_count_formula() { ); } } + +#[test] +fn test_factoring_to_ilp_bf_vs_ilp() { + let problem = Factoring::new(2, 2, 6); + let reduction: ReductionFactoringToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/hamiltonianpath_ilp.rs b/src/unit_tests/rules/hamiltonianpath_ilp.rs index bc25df0ed..4e62efbcc 100644 --- a/src/unit_tests/rules/hamiltonianpath_ilp.rs +++ b/src/unit_tests/rules/hamiltonianpath_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Or; @@ -62,6 +62,18 @@ fn test_hamiltonianpath_to_ilp_cycle_graph() { assert_eq!(problem.evaluate(&extracted), Or(true)); } +#[test] +fn test_hamiltonianpath_to_ilp_bf_vs_ilp() { + let problem = HamiltonianPath::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])); + let reduction: ReductionHamiltonianPathToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} + #[test] fn test_hamiltonianpath_to_ilp_no_path() { // Disconnected graph: no Hamiltonian path diff --git a/src/unit_tests/rules/integralflowbundles_ilp.rs b/src/unit_tests/rules/integralflowbundles_ilp.rs index 944755ef3..44d38a480 100644 --- a/src/unit_tests/rules/integralflowbundles_ilp.rs +++ b/src/unit_tests/rules/integralflowbundles_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{Comparison, ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -111,3 +111,15 @@ fn test_integral_flow_bundles_to_ilp_sink_requirement_constraint() { assert_eq!(sink_constraint.rhs, 1.0); assert_eq!(sink_constraint.terms, vec![(2, 1.0), (3, 1.0)]); } + +#[test] +fn test_integralflowbundles_to_ilp_bf_vs_ilp() { + let problem = yes_instance(); + let reduction: ReductionIFBToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/integralflowhomologousarcs_ilp.rs b/src/unit_tests/rules/integralflowhomologousarcs_ilp.rs index 2975222a1..4abb45d54 100644 --- a/src/unit_tests/rules/integralflowhomologousarcs_ilp.rs +++ b/src/unit_tests/rules/integralflowhomologousarcs_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -30,3 +30,22 @@ fn test_integralflowhomologousarcs_to_ilp_closed_loop() { assert!(source.evaluate(&extracted)); } + +#[test] +fn test_integralflowhomologousarcs_to_ilp_bf_vs_ilp() { + let source = IntegralFlowHomologousArcs::new( + DirectedGraph::new(4, vec![(0, 1), (0, 2), (1, 3), (2, 3)]), + vec![2, 2, 2, 2], + 0, + 3, + 2, + vec![(0, 1)], + ); + let reduction = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/integralflowwithmultipliers_ilp.rs b/src/unit_tests/rules/integralflowwithmultipliers_ilp.rs index 82693d36f..e22c31189 100644 --- a/src/unit_tests/rules/integralflowwithmultipliers_ilp.rs +++ b/src/unit_tests/rules/integralflowwithmultipliers_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -29,3 +29,22 @@ fn test_integralflowwithmultipliers_to_ilp_closed_loop() { assert!(source.evaluate(&extracted)); } + +#[test] +fn test_integralflowwithmultipliers_to_ilp_bf_vs_ilp() { + let source = IntegralFlowWithMultipliers::new( + DirectedGraph::new(4, vec![(0, 1), (0, 2), (1, 3), (2, 3)]), + 0, + 3, + vec![1, 1, 1, 1], + vec![2, 2, 2, 2], + 2, + ); + let reduction = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs b/src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs index 4f3a2525c..1817205fc 100644 --- a/src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs +++ b/src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs @@ -2,7 +2,9 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; +use crate::traits::Problem; #[test] fn test_lengthboundeddisjointpaths_to_ilp_closed_loop() { @@ -20,3 +22,20 @@ fn test_lengthboundeddisjointpaths_to_ilp_closed_loop() { "LengthBoundedDisjointPaths->ILP closed loop", ); } + +#[test] +fn test_lengthboundeddisjointpaths_to_ilp_bf_vs_ilp() { + let source = LengthBoundedDisjointPaths::new( + SimpleGraph::new(4, vec![(0, 1), (0, 2), (1, 3), (2, 3)]), + 0, + 3, + 2, + ); + let reduction = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/longestcircuit_ilp.rs b/src/unit_tests/rules/longestcircuit_ilp.rs index 1f9ae4f9e..86b562d99 100644 --- a/src/unit_tests/rules/longestcircuit_ilp.rs +++ b/src/unit_tests/rules/longestcircuit_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -89,3 +89,18 @@ fn test_solution_extraction() { let extracted = reduction.extract_solution(&ilp_solution); assert!(problem.evaluate(&extracted).0.is_some()); } + +#[test] +fn test_longestcircuit_to_ilp_bf_vs_ilp() { + let problem = LongestCircuit::new( + SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]), + vec![1, 1, 1], + ); + let reduction: ReductionLongestCircuitToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/longestcommonsubsequence_ilp.rs b/src/unit_tests/rules/longestcommonsubsequence_ilp.rs index 265926c94..f59044fdd 100644 --- a/src/unit_tests/rules/longestcommonsubsequence_ilp.rs +++ b/src/unit_tests/rules/longestcommonsubsequence_ilp.rs @@ -93,3 +93,15 @@ fn test_lcs_to_ilp_single_position_all_padding() { let value = problem.evaluate(&extracted); assert_eq!(value, Max(Some(0))); } + +#[test] +fn test_longestcommonsubsequence_to_ilp_bf_vs_ilp() { + let problem = LongestCommonSubsequence::new(2, vec![vec![0, 1, 0], vec![1, 0, 1]]); + let reduction: ReductionLCSToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/longestpath_ilp.rs b/src/unit_tests/rules/longestpath_ilp.rs index d2aea4305..3aae63b88 100644 --- a/src/unit_tests/rules/longestpath_ilp.rs +++ b/src/unit_tests/rules/longestpath_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Max; @@ -106,3 +106,15 @@ fn test_source_equals_target_uses_empty_path() { assert_eq!(extracted, vec![0, 0, 0]); assert_eq!(problem.evaluate(&extracted), Max(Some(0))); } + +#[test] +fn test_longestpath_to_ilp_bf_vs_ilp() { + let problem = simple_path_problem(); + let reduction: ReductionLongestPathToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/maximumclique_ilp.rs b/src/unit_tests/rules/maximumclique_ilp.rs index ce819c101..ae6639200 100644 --- a/src/unit_tests/rules/maximumclique_ilp.rs +++ b/src/unit_tests/rules/maximumclique_ilp.rs @@ -1,5 +1,6 @@ use super::*; -use crate::solvers::ILPSolver; +use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::traits::Problem; /// Check if a configuration represents a valid clique in the graph. /// A clique is valid if all selected vertices are pairwise adjacent. @@ -306,3 +307,18 @@ fn test_star_graph() { assert!(is_valid_clique(&problem, &extracted)); assert_eq!(clique_size(&problem, &extracted), 2); } + +#[test] +fn test_maximumclique_to_ilp_bf_vs_ilp() { + let problem: MaximumClique = MaximumClique::new( + SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]), + vec![1; 4], + ); + let reduction: ReductionCliqueToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/maximumindependentset_ilp.rs b/src/unit_tests/rules/maximumindependentset_ilp.rs index 5d2146374..d3361d850 100644 --- a/src/unit_tests/rules/maximumindependentset_ilp.rs +++ b/src/unit_tests/rules/maximumindependentset_ilp.rs @@ -1,7 +1,7 @@ use crate::models::algebraic::{ObjectiveSense, ILP}; use crate::models::graph::MaximumIndependentSet; use crate::rules::{MinimizeSteps, ReductionChain, ReductionGraph, ReductionPath}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::{Max, ProblemSize}; @@ -89,3 +89,17 @@ fn test_maximumindependentset_to_ilp_via_path_weighted() { assert_eq!(problem.evaluate(&extracted), Max(Some(100))); assert_eq!(extracted, vec![0, 1, 0]); } + +#[test] +fn test_maximumindependentset_to_ilp_bf_vs_ilp() { + let problem = MaximumIndependentSet::new( + SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]), + vec![1i32; 4], + ); + let (_, chain) = reduce_mis_to_ilp(&problem); + let ilp: &ILP = chain.target_problem(); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new().solve(ilp).expect("ILP should be solvable"); + let extracted = chain.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/maximummatching_ilp.rs b/src/unit_tests/rules/maximummatching_ilp.rs index dab0c1680..37b22b5f2 100644 --- a/src/unit_tests/rules/maximummatching_ilp.rs +++ b/src/unit_tests/rules/maximummatching_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Max; @@ -248,3 +248,16 @@ fn test_solve_reduced() { assert!(problem.evaluate(&solution).is_valid()); assert_eq!(problem.evaluate(&solution), Max(Some(2))); } + +#[test] +fn test_maximummatching_to_ilp_bf_vs_ilp() { + let problem = + MaximumMatching::<_, i32>::unit_weights(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])); + let reduction: ReductionMatchingToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/maximumsetpacking_ilp.rs b/src/unit_tests/rules/maximumsetpacking_ilp.rs index ca288083c..578a60c49 100644 --- a/src/unit_tests/rules/maximumsetpacking_ilp.rs +++ b/src/unit_tests/rules/maximumsetpacking_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; use crate::types::Max; @@ -127,3 +127,15 @@ fn test_solve_reduced() { assert!(problem.evaluate(&solution).is_valid()); assert_eq!(problem.evaluate(&solution), Max(Some(2))); } + +#[test] +fn test_maximumsetpacking_to_ilp_bf_vs_ilp() { + let problem = MaximumSetPacking::::new(vec![vec![0, 1], vec![1, 2], vec![2, 3]]); + let reduction: ReductionSPToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs b/src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs index 718693d1a..dd205b6ab 100644 --- a/src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs +++ b/src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs @@ -3,6 +3,7 @@ use crate::models::algebraic::ILP; use crate::models::graph::MinimumCutIntoBoundedSets; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -66,3 +67,15 @@ fn test_larger_instance() { "MinCutBS larger instance", ); } + +#[test] +fn test_minimumcutintoboundedsets_to_ilp_bf_vs_ilp() { + let source = small_instance(); + let reduction: ReductionMinCutBSToILP = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/minimumdominatingset_ilp.rs b/src/unit_tests/rules/minimumdominatingset_ilp.rs index 073c304ce..87ac865ed 100644 --- a/src/unit_tests/rules/minimumdominatingset_ilp.rs +++ b/src/unit_tests/rules/minimumdominatingset_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; use crate::types::Min; @@ -241,3 +241,18 @@ fn test_cycle_graph() { assert!(problem.evaluate(&extracted).is_valid()); } + +#[test] +fn test_minimumdominatingset_to_ilp_bf_vs_ilp() { + let problem = MinimumDominatingSet::new( + SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3)]), + vec![1i32; 4], + ); + let reduction: ReductionDSToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs b/src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs index 762157e7e..aa678b103 100644 --- a/src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs +++ b/src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::DirectedGraph; use crate::traits::Problem; use crate::types::Min; @@ -193,3 +193,16 @@ fn test_solution_extraction() { // Verify this is a valid FVS (removing vertex 0 breaks the 3-cycle) assert!(problem.evaluate(&extracted).is_valid()); } + +#[test] +fn test_minimumfeedbackvertexset_to_ilp_bf_vs_ilp() { + let graph = DirectedGraph::new(3, vec![(0, 1), (1, 2), (2, 0)]); + let problem = MinimumFeedbackVertexSet::new(graph, vec![1i32; 3]); + let reduction: ReductionMFVSToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/minimummultiwaycut_ilp.rs b/src/unit_tests/rules/minimummultiwaycut_ilp.rs index 4ed31f8b7..b974d9033 100644 --- a/src/unit_tests/rules/minimummultiwaycut_ilp.rs +++ b/src/unit_tests/rules/minimummultiwaycut_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::ObjectiveSense; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Min; @@ -137,3 +137,15 @@ fn test_solve_reduced() { assert!(problem.evaluate(&solution).is_valid()); assert_eq!(problem.evaluate(&solution), Min(Some(8))); } + +#[test] +fn test_minimummultiwaycut_to_ilp_bf_vs_ilp() { + let problem = canonical_instance(); + let reduction: ReductionMMCToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/minimumsetcovering_ilp.rs b/src/unit_tests/rules/minimumsetcovering_ilp.rs index eda045703..8382fb92f 100644 --- a/src/unit_tests/rules/minimumsetcovering_ilp.rs +++ b/src/unit_tests/rules/minimumsetcovering_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; use crate::types::Min; @@ -225,3 +225,15 @@ fn test_constraint_structure() { assert!(!vars2.contains(&1)); assert!(vars2.contains(&2)); } + +#[test] +fn test_minimumsetcovering_to_ilp_bf_vs_ilp() { + let problem = MinimumSetCovering::::new(3, vec![vec![0, 1], vec![1, 2], vec![0, 2]]); + let reduction: ReductionSCToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/minimumvertexcover_ilp.rs b/src/unit_tests/rules/minimumvertexcover_ilp.rs index 57f47c507..119d5fa86 100644 --- a/src/unit_tests/rules/minimumvertexcover_ilp.rs +++ b/src/unit_tests/rules/minimumvertexcover_ilp.rs @@ -86,3 +86,18 @@ fn test_minimumvertexcover_to_ilp_via_path_weighted() { assert_eq!(problem.evaluate(&extracted), Min(Some(1))); assert_eq!(extracted, vec![0, 1, 0]); } + +#[test] +fn test_minimumvertexcover_to_ilp_bf_vs_ilp() { + let problem = MinimumVertexCover::new( + SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]), + vec![1i32; 4], + ); + let (_, chain) = reduce_vc_to_ilp(&problem); + let ilp: &ILP = chain.target_problem(); + let bf_solutions = BruteForce::new().find_all_witnesses(&problem); + let bf_value = problem.evaluate(&bf_solutions[0]); + let ilp_solution = ILPSolver::new().solve(ilp).expect("ILP should be solvable"); + let extracted = chain.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/optimallineararrangement_ilp.rs b/src/unit_tests/rules/optimallineararrangement_ilp.rs index 2e92b517e..18abb220f 100644 --- a/src/unit_tests/rules/optimallineararrangement_ilp.rs +++ b/src/unit_tests/rules/optimallineararrangement_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -71,7 +71,6 @@ fn test_optimallineararrangement_to_ilp_optimization() { // Cannot brute-force ILP (integer domain too large), so compare BF source vs ILP solver let bf = BruteForce::new(); - use crate::Solver; let bf_value = bf.solve(&problem); let ilp_solver = ILPSolver::new(); @@ -97,3 +96,15 @@ fn test_solution_extraction() { let extracted = reduction.extract_solution(&ilp_solution); assert!(problem.evaluate(&extracted).0.is_some()); } + +#[test] +fn test_optimallineararrangement_to_ilp_bf_vs_ilp() { + let problem = OptimalLinearArrangement::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])); + let reduction: ReductionOLAToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/pathconstrainednetworkflow_ilp.rs b/src/unit_tests/rules/pathconstrainednetworkflow_ilp.rs index 98757b34b..afc829c38 100644 --- a/src/unit_tests/rules/pathconstrainednetworkflow_ilp.rs +++ b/src/unit_tests/rules/pathconstrainednetworkflow_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -29,3 +29,22 @@ fn test_pathconstrainednetworkflow_to_ilp_closed_loop() { assert!(source.evaluate(&extracted)); } + +#[test] +fn test_pathconstrainednetworkflow_to_ilp_bf_vs_ilp() { + let source = PathConstrainedNetworkFlow::new( + DirectedGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]), + vec![1, 1, 1], + 0, + 2, + vec![vec![0, 1], vec![2]], + 2, + ); + let reduction = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/precedenceconstrainedscheduling_ilp.rs b/src/unit_tests/rules/precedenceconstrainedscheduling_ilp.rs index b92d00f3b..5793a6567 100644 --- a/src/unit_tests/rules/precedenceconstrainedscheduling_ilp.rs +++ b/src/unit_tests/rules/precedenceconstrainedscheduling_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; fn feasible_instance() -> PrecedenceConstrainedScheduling { @@ -77,3 +77,15 @@ fn test_precedenceconstrainedscheduling_to_ilp_extract_solution() { "manually constructed solution should be valid" ); } + +#[test] +fn test_precedenceconstrainedscheduling_to_ilp_bf_vs_ilp() { + let problem = feasible_instance(); + let reduction: ReductionPCSToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/quadraticassignment_ilp.rs b/src/unit_tests/rules/quadraticassignment_ilp.rs index fd0bade79..0bcf1f270 100644 --- a/src/unit_tests/rules/quadraticassignment_ilp.rs +++ b/src/unit_tests/rules/quadraticassignment_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; fn small_qap() -> QuadraticAssignment { @@ -105,3 +105,15 @@ fn test_quadraticassignment_to_ilp_rectangular() { assert!(ilp_value.is_valid()); assert_eq!(ilp_value, bf_value); } + +#[test] +fn test_quadraticassignment_to_ilp_bf_vs_ilp() { + let problem = small_qap(); + let reduction: ReductionQAPToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/ruralpostman_ilp.rs b/src/unit_tests/rules/ruralpostman_ilp.rs index e2d2a5252..61dd83864 100644 --- a/src/unit_tests/rules/ruralpostman_ilp.rs +++ b/src/unit_tests/rules/ruralpostman_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -56,3 +56,19 @@ fn test_ruralpostman_to_ilp_optimization() { "ILP optimum must match brute-force optimum" ); } + +#[test] +fn test_ruralpostman_to_ilp_bf_vs_ilp() { + let source = RuralPostman::new( + SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]), + vec![1, 1, 1], + vec![0], + ); + let reduction = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/schedulingwithindividualdeadlines_ilp.rs b/src/unit_tests/rules/schedulingwithindividualdeadlines_ilp.rs index 884ee8c47..7a86b61f4 100644 --- a/src/unit_tests/rules/schedulingwithindividualdeadlines_ilp.rs +++ b/src/unit_tests/rules/schedulingwithindividualdeadlines_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; fn feasible_instance() -> SchedulingWithIndividualDeadlines { @@ -78,3 +78,15 @@ fn test_schedulingwithindividualdeadlines_to_ilp_extract_solution() { "manually constructed solution is valid" ); } + +#[test] +fn test_schedulingwithindividualdeadlines_to_ilp_bf_vs_ilp() { + let problem = feasible_instance(); + let reduction: ReductionSWIDToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/sequencingtominimizeweightedcompletiontime_ilp.rs b/src/unit_tests/rules/sequencingtominimizeweightedcompletiontime_ilp.rs index 3f39efa50..96f8653bf 100644 --- a/src/unit_tests/rules/sequencingtominimizeweightedcompletiontime_ilp.rs +++ b/src/unit_tests/rules/sequencingtominimizeweightedcompletiontime_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; use crate::models::misc::SequencingToMinimizeWeightedCompletionTime; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; use crate::types::Min; @@ -157,3 +157,15 @@ fn test_solve_reduced_matches_source_optimum() { assert_eq!(source_solution, vec![1, 2, 0, 1, 0]); assert_eq!(problem.evaluate(&source_solution), Min(Some(46))); } + +#[test] +fn test_sequencingtominimizeweightedcompletiontime_to_ilp_bf_vs_ilp() { + let problem = SequencingToMinimizeWeightedCompletionTime::new(vec![2, 1], vec![3, 5], vec![]); + let reduction: ReductionSTMWCTToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/sequencingwithinintervals_ilp.rs b/src/unit_tests/rules/sequencingwithinintervals_ilp.rs index 767c24a9b..8a4b529de 100644 --- a/src/unit_tests/rules/sequencingwithinintervals_ilp.rs +++ b/src/unit_tests/rules/sequencingwithinintervals_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::traits::Problem; fn feasible_instance() -> SequencingWithinIntervals { @@ -89,3 +89,15 @@ fn test_sequencingwithinintervals_to_ilp_extract_solution() { "manually constructed solution is valid" ); } + +#[test] +fn test_sequencingwithinintervals_to_ilp_bf_vs_ilp() { + let problem = feasible_instance(); + let reduction: ReductionSWIToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/stackercrane_ilp.rs b/src/unit_tests/rules/stackercrane_ilp.rs index ca7e7d1aa..1cf2aa8bd 100644 --- a/src/unit_tests/rules/stackercrane_ilp.rs +++ b/src/unit_tests/rules/stackercrane_ilp.rs @@ -2,6 +2,8 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; +use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::traits::Problem; #[test] fn test_stackercrane_to_ilp_closed_loop() { @@ -14,3 +16,15 @@ fn test_stackercrane_to_ilp_closed_loop() { "StackerCrane->ILP closed loop", ); } + +#[test] +fn test_stackercrane_to_ilp_bf_vs_ilp() { + let source = StackerCrane::new(3, vec![(0, 1), (2, 0)], vec![(1, 2)], vec![1, 1], vec![1]); + let reduction = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/steinertree_ilp.rs b/src/unit_tests/rules/steinertree_ilp.rs index 24a7436a0..87423a563 100644 --- a/src/unit_tests/rules/steinertree_ilp.rs +++ b/src/unit_tests/rules/steinertree_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; use crate::models::graph::SteinerTree; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Min; @@ -95,3 +95,15 @@ fn test_reduction_rejects_zero_weights() { let problem = SteinerTree::new(graph, vec![0, 0, 0], vec![0, 1]); let _ = ReduceTo::>::reduce_to(&problem); } + +#[test] +fn test_steinertree_to_ilp_bf_vs_ilp() { + let problem = canonical_instance(); + let reduction: ReductionSteinerTreeToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/steinertreeingraphs_ilp.rs b/src/unit_tests/rules/steinertreeingraphs_ilp.rs index 7f07c5400..b0bd69cd5 100644 --- a/src/unit_tests/rules/steinertreeingraphs_ilp.rs +++ b/src/unit_tests/rules/steinertreeingraphs_ilp.rs @@ -2,7 +2,9 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; +use crate::traits::Problem; #[test] fn test_steinertreeingraphs_to_ilp_closed_loop() { @@ -21,3 +23,19 @@ fn test_steinertreeingraphs_to_ilp_closed_loop() { "SteinerTreeInGraphs->ILP closed loop", ); } + +#[test] +fn test_steinertreeingraphs_to_ilp_bf_vs_ilp() { + let source = SteinerTreeInGraphs::new( + SimpleGraph::new(3, vec![(0, 1), (1, 2)]), + vec![0, 2], + vec![1, 1], + ); + let reduction = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/strongconnectivityaugmentation_ilp.rs b/src/unit_tests/rules/strongconnectivityaugmentation_ilp.rs index cb765f5c8..90fbff51b 100644 --- a/src/unit_tests/rules/strongconnectivityaugmentation_ilp.rs +++ b/src/unit_tests/rules/strongconnectivityaugmentation_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::models::graph::StrongConnectivityAugmentation; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -92,3 +92,15 @@ fn test_infeasible_budget() { let solver = ILPSolver::new(); assert!(solver.solve(ilp).is_none()); } + +#[test] +fn test_strongconnectivityaugmentation_to_ilp_bf_vs_ilp() { + let source = small_instance(); + let reduction: ReductionSCAToILP = ReduceTo::>::reduce_to(&source); + let bf_value = BruteForce::new().solve(&source); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/subgraphisomorphism_ilp.rs b/src/unit_tests/rules/subgraphisomorphism_ilp.rs index ffab59160..6c80e08a7 100644 --- a/src/unit_tests/rules/subgraphisomorphism_ilp.rs +++ b/src/unit_tests/rules/subgraphisomorphism_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Or; @@ -94,3 +94,17 @@ fn test_solution_extraction() { let extracted = reduction.extract_solution(&ilp_solution); assert_eq!(problem.evaluate(&extracted), Or(true)); } + +#[test] +fn test_subgraphisomorphism_to_ilp_bf_vs_ilp() { + let host = SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3), (3, 0)]); + let pattern = SimpleGraph::new(3, vec![(0, 1), (1, 2)]); + let problem = SubgraphIsomorphism::new(host, pattern); + let reduction: ReductionSubIsoToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/travelingsalesman_ilp.rs b/src/unit_tests/rules/travelingsalesman_ilp.rs index c81a477f4..7afdd6faf 100644 --- a/src/unit_tests/rules/travelingsalesman_ilp.rs +++ b/src/unit_tests/rules/travelingsalesman_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Min; @@ -147,3 +147,18 @@ fn test_solve_reduced() { let bf_solutions = bf.find_all_witnesses(&problem); assert_eq!(metric, problem.evaluate(&bf_solutions[0])); } + +#[test] +fn test_travelingsalesman_to_ilp_bf_vs_ilp() { + let problem = TravelingSalesman::<_, i32>::unit_weights(SimpleGraph::new( + 4, + vec![(0, 1), (1, 2), (2, 3), (3, 0)], + )); + let reduction: ReductionTSPToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/undirectedflowlowerbounds_ilp.rs b/src/unit_tests/rules/undirectedflowlowerbounds_ilp.rs index 38a0a3c3b..2003c2d57 100644 --- a/src/unit_tests/rules/undirectedflowlowerbounds_ilp.rs +++ b/src/unit_tests/rules/undirectedflowlowerbounds_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -94,3 +94,15 @@ fn test_undirectedflowlowerbounds_to_ilp_extract_solution() { "manually extracted orientation should be valid" ); } + +#[test] +fn test_undirectedflowlowerbounds_to_ilp_bf_vs_ilp() { + let problem = feasible_instance(); + let reduction: ReductionUFLBToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} diff --git a/src/unit_tests/rules/undirectedtwocommodityintegralflow_ilp.rs b/src/unit_tests/rules/undirectedtwocommodityintegralflow_ilp.rs index 1968700f1..2ce7bf76f 100644 --- a/src/unit_tests/rules/undirectedtwocommodityintegralflow_ilp.rs +++ b/src/unit_tests/rules/undirectedtwocommodityintegralflow_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver}; +use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -129,3 +129,15 @@ fn test_undirectedtwocommodityintegralflow_to_ilp_extract_solution() { "manually extracted solution should be valid" ); } + +#[test] +fn test_undirectedtwocommodityintegralflow_to_ilp_bf_vs_ilp() { + let problem = feasible_instance(); + let reduction: ReductionU2CIFToILP = ReduceTo::>::reduce_to(&problem); + let bf_value = BruteForce::new().solve(&problem); + let ilp_solution = ILPSolver::new() + .solve(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(problem.evaluate(&extracted), bf_value); +} From e672b8c671dcfa084320edb6a4861a86fabdb817 Mon Sep 17 00:00:00 2001 From: Xiwei Pan Date: Thu, 26 Mar 2026 22:38:03 +0800 Subject: [PATCH 2/3] 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) --- src/rules/test_helpers.rs | 17 +++++++++++++++++ src/unit_tests/rules/acyclicpartition_ilp.rs | 9 ++------- .../balancedcompletebipartitesubgraph_ilp.rs | 8 +------- src/unit_tests/rules/bicliquecover_ilp.rs | 8 +------- .../rules/biconnectivityaugmentation_ilp.rs | 9 ++------- src/unit_tests/rules/binpacking_ilp.rs | 9 ++------- .../rules/bottlenecktravelingsalesman_ilp.rs | 9 ++------- .../rules/boundedcomponentspanningforest_ilp.rs | 9 ++------- src/unit_tests/rules/capacityassignment_ilp.rs | 9 ++------- src/unit_tests/rules/coloring_ilp.rs | 9 ++------- .../directedtwocommodityintegralflow_ilp.rs | 9 ++------- .../rules/disjointconnectingpaths_ilp.rs | 9 +-------- src/unit_tests/rules/factoring_ilp.rs | 10 ++-------- src/unit_tests/rules/hamiltonianpath_ilp.rs | 9 ++------- src/unit_tests/rules/integralflowbundles_ilp.rs | 9 ++------- .../rules/integralflowhomologousarcs_ilp.rs | 9 ++------- .../rules/integralflowwithmultipliers_ilp.rs | 9 ++------- .../rules/lengthboundeddisjointpaths_ilp.rs | 9 +-------- src/unit_tests/rules/longestcircuit_ilp.rs | 9 ++------- .../rules/longestcommonsubsequence_ilp.rs | 7 +------ src/unit_tests/rules/longestpath_ilp.rs | 9 ++------- src/unit_tests/rules/maximumclique_ilp.rs | 10 ++-------- src/unit_tests/rules/maximummatching_ilp.rs | 9 ++------- src/unit_tests/rules/maximumsetpacking_ilp.rs | 9 ++------- .../rules/minimumcutintoboundedsets_ilp.rs | 8 +------- .../rules/minimumdominatingset_ilp.rs | 9 ++------- .../rules/minimumfeedbackvertexset_ilp.rs | 9 ++------- src/unit_tests/rules/minimummultiwaycut_ilp.rs | 9 ++------- src/unit_tests/rules/minimumsetcovering_ilp.rs | 9 ++------- .../rules/optimallineararrangement_ilp.rs | 7 +------ .../rules/pathconstrainednetworkflow_ilp.rs | 9 ++------- .../precedenceconstrainedscheduling_ilp.rs | 9 ++------- src/unit_tests/rules/quadraticassignment_ilp.rs | 9 ++------- src/unit_tests/rules/ruralpostman_ilp.rs | 9 ++------- .../schedulingwithindividualdeadlines_ilp.rs | 9 ++------- ...ncingtominimizeweightedcompletiontime_ilp.rs | 9 ++------- .../rules/sequencingwithinintervals_ilp.rs | 9 ++------- src/unit_tests/rules/stackercrane_ilp.rs | 9 +-------- src/unit_tests/rules/steinertree_ilp.rs | 9 ++------- src/unit_tests/rules/steinertreeingraphs_ilp.rs | 9 +-------- .../rules/strongconnectivityaugmentation_ilp.rs | 9 ++------- src/unit_tests/rules/subgraphisomorphism_ilp.rs | 9 ++------- src/unit_tests/rules/travelingsalesman_ilp.rs | 9 ++------- .../rules/undirectedflowlowerbounds_ilp.rs | 9 ++------- .../undirectedtwocommodityintegralflow_ilp.rs | 9 ++------- 45 files changed, 96 insertions(+), 312 deletions(-) diff --git a/src/rules/test_helpers.rs b/src/rules/test_helpers.rs index 6053ad187..9fb71e316 100644 --- a/src/rules/test_helpers.rs +++ b/src/rules/test_helpers.rs @@ -193,6 +193,23 @@ pub(crate) fn assert_satisfaction_round_trip_from_satisfaction_target( ); } +#[cfg(feature = "ilp-solver")] +pub(crate) fn assert_bf_vs_ilp(source: &R::Source, reduction: &R) +where + R: ReductionResult, + R::Source: Problem + 'static, + R::Target: 'static, + ::Value: Aggregate + std::fmt::Debug + PartialEq, +{ + use crate::solvers::{ILPSolver, Solver}; + let bf_value = BruteForce::new().solve(source); + let ilp_solution = ILPSolver::new() + .solve_dyn(reduction.target_problem()) + .expect("ILP should be solvable"); + let extracted = reduction.extract_solution(&ilp_solution); + assert_eq!(source.evaluate(&extracted), bf_value); +} + pub(crate) fn solve_optimization_problem

(problem: &P) -> Option> where P: Problem + 'static, diff --git a/src/unit_tests/rules/acyclicpartition_ilp.rs b/src/unit_tests/rules/acyclicpartition_ilp.rs index a0d3447c5..97efc979f 100644 --- a/src/unit_tests/rules/acyclicpartition_ilp.rs +++ b/src/unit_tests/rules/acyclicpartition_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::models::graph::AcyclicPartition; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -83,10 +83,5 @@ fn test_infeasible_instance() { fn test_acyclicpartition_to_ilp_bf_vs_ilp() { let source = small_instance(); let reduction: ReductionAcyclicPartitionToILP = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs b/src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs index b29207edb..9c4cc1109 100644 --- a/src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs +++ b/src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs @@ -3,7 +3,6 @@ use crate::models::algebraic::ILP; use crate::models::graph::BalancedCompleteBipartiteSubgraph; use crate::rules::test_helpers::assert_satisfaction_round_trip_from_satisfaction_target; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::BipartiteGraph; use crate::traits::Problem; @@ -64,10 +63,5 @@ fn test_extract_solution_identity() { fn test_balancedcompletebipartitesubgraph_to_ilp_bf_vs_ilp() { let source = small_instance(); let reduction: ReductionBCBSToILP = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/bicliquecover_ilp.rs b/src/unit_tests/rules/bicliquecover_ilp.rs index c39ac3cb7..db542b597 100644 --- a/src/unit_tests/rules/bicliquecover_ilp.rs +++ b/src/unit_tests/rules/bicliquecover_ilp.rs @@ -3,7 +3,6 @@ use crate::models::algebraic::ILP; use crate::models::graph::BicliqueCover; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::BipartiteGraph; use crate::traits::Problem; @@ -67,10 +66,5 @@ fn test_single_edge() { fn test_bicliquecover_to_ilp_bf_vs_ilp() { let source = small_instance(); let reduction: ReductionBicliqueCoverToILP = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/biconnectivityaugmentation_ilp.rs b/src/unit_tests/rules/biconnectivityaugmentation_ilp.rs index bad3c90d2..fa21abed9 100644 --- a/src/unit_tests/rules/biconnectivityaugmentation_ilp.rs +++ b/src/unit_tests/rules/biconnectivityaugmentation_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::models::graph::BiconnectivityAugmentation; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -82,10 +82,5 @@ fn test_already_biconnected() { fn test_biconnectivityaugmentation_to_ilp_bf_vs_ilp() { let source = small_instance(); let reduction: ReductionBiconnAugToILP = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/binpacking_ilp.rs b/src/unit_tests/rules/binpacking_ilp.rs index 26f190dc5..772eb4601 100644 --- a/src/unit_tests/rules/binpacking_ilp.rs +++ b/src/unit_tests/rules/binpacking_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; use crate::types::Min; @@ -146,10 +146,5 @@ fn test_solve_reduced() { fn test_binpacking_to_ilp_bf_vs_ilp() { let problem = BinPacking::new(vec![3, 3, 2], 5); let reduction: ReductionBPToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs b/src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs index 1642bad50..70452a9e1 100644 --- a/src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs +++ b/src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -101,10 +101,5 @@ fn test_no_hamiltonian_cycle_infeasible() { fn test_bottlenecktravelingsalesman_to_ilp_bf_vs_ilp() { let problem = k4_btsp(); let reduction: ReductionBTSPToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs b/src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs index 3594e1b14..bd97a4a96 100644 --- a/src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs +++ b/src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::models::graph::BoundedComponentSpanningForest; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -88,10 +88,5 @@ fn test_infeasible_instance() { fn test_boundedcomponentspanningforest_to_ilp_bf_vs_ilp() { let source = small_instance(); let reduction: ReductionBCSFToILP = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/capacityassignment_ilp.rs b/src/unit_tests/rules/capacityassignment_ilp.rs index 7667cafc4..a5efbb8bc 100644 --- a/src/unit_tests/rules/capacityassignment_ilp.rs +++ b/src/unit_tests/rules/capacityassignment_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; use crate::types::Min; @@ -111,10 +111,5 @@ fn test_capacityassignment_to_ilp_bf_vs_ilp() { 12, ); let reduction: ReductionCAToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/coloring_ilp.rs b/src/unit_tests/rules/coloring_ilp.rs index ed6ca8561..f436f39b5 100644 --- a/src/unit_tests/rules/coloring_ilp.rs +++ b/src/unit_tests/rules/coloring_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; use crate::variant::{K1, K2, K3, K4}; @@ -276,10 +276,5 @@ fn test_single_edge() { fn test_coloring_to_ilp_bf_vs_ilp() { let problem = KColoring::::new(SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)])); let reduction = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs b/src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs index 5dbe97b43..27b0972bc 100644 --- a/src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs +++ b/src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -123,10 +123,5 @@ fn test_directedtwocommodityintegralflow_to_ilp_extract_solution() { fn test_directedtwocommodityintegralflow_to_ilp_bf_vs_ilp() { let problem = feasible_instance(); let reduction: ReductionD2CIFToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/disjointconnectingpaths_ilp.rs b/src/unit_tests/rules/disjointconnectingpaths_ilp.rs index 9b9081e6c..d7aab0c03 100644 --- a/src/unit_tests/rules/disjointconnectingpaths_ilp.rs +++ b/src/unit_tests/rules/disjointconnectingpaths_ilp.rs @@ -2,9 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::test_helpers::assert_satisfaction_round_trip_from_satisfaction_target; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; -use crate::traits::Problem; #[test] fn test_disjointconnectingpaths_to_ilp_closed_loop() { @@ -30,10 +28,5 @@ fn test_disjointconnectingpaths_to_ilp_bf_vs_ilp() { vec![(0, 2), (3, 5)], ); let reduction = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/factoring_ilp.rs b/src/unit_tests/rules/factoring_ilp.rs index 866cc876d..85bc86003 100644 --- a/src/unit_tests/rules/factoring_ilp.rs +++ b/src/unit_tests/rules/factoring_ilp.rs @@ -1,6 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; -use crate::traits::Problem; +use crate::solvers::{BruteForce, ILPSolver}; #[test] fn test_reduction_creates_valid_ilp() { @@ -303,10 +302,5 @@ fn test_variable_count_formula() { fn test_factoring_to_ilp_bf_vs_ilp() { let problem = Factoring::new(2, 2, 6); let reduction: ReductionFactoringToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/hamiltonianpath_ilp.rs b/src/unit_tests/rules/hamiltonianpath_ilp.rs index 4e62efbcc..ce36c3422 100644 --- a/src/unit_tests/rules/hamiltonianpath_ilp.rs +++ b/src/unit_tests/rules/hamiltonianpath_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Or; @@ -66,12 +66,7 @@ fn test_hamiltonianpath_to_ilp_cycle_graph() { fn test_hamiltonianpath_to_ilp_bf_vs_ilp() { let problem = HamiltonianPath::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])); let reduction: ReductionHamiltonianPathToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } #[test] diff --git a/src/unit_tests/rules/integralflowbundles_ilp.rs b/src/unit_tests/rules/integralflowbundles_ilp.rs index 44d38a480..6a3268ebf 100644 --- a/src/unit_tests/rules/integralflowbundles_ilp.rs +++ b/src/unit_tests/rules/integralflowbundles_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{Comparison, ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -116,10 +116,5 @@ fn test_integral_flow_bundles_to_ilp_sink_requirement_constraint() { fn test_integralflowbundles_to_ilp_bf_vs_ilp() { let problem = yes_instance(); let reduction: ReductionIFBToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/integralflowhomologousarcs_ilp.rs b/src/unit_tests/rules/integralflowhomologousarcs_ilp.rs index 4abb45d54..4ddd58699 100644 --- a/src/unit_tests/rules/integralflowhomologousarcs_ilp.rs +++ b/src/unit_tests/rules/integralflowhomologousarcs_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -42,10 +42,5 @@ fn test_integralflowhomologousarcs_to_ilp_bf_vs_ilp() { vec![(0, 1)], ); let reduction = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/integralflowwithmultipliers_ilp.rs b/src/unit_tests/rules/integralflowwithmultipliers_ilp.rs index e22c31189..35c1b0cb8 100644 --- a/src/unit_tests/rules/integralflowwithmultipliers_ilp.rs +++ b/src/unit_tests/rules/integralflowwithmultipliers_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -41,10 +41,5 @@ fn test_integralflowwithmultipliers_to_ilp_bf_vs_ilp() { 2, ); let reduction = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs b/src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs index 1817205fc..206743262 100644 --- a/src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs +++ b/src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs @@ -2,9 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; -use crate::traits::Problem; #[test] fn test_lengthboundeddisjointpaths_to_ilp_closed_loop() { @@ -32,10 +30,5 @@ fn test_lengthboundeddisjointpaths_to_ilp_bf_vs_ilp() { 2, ); let reduction = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/longestcircuit_ilp.rs b/src/unit_tests/rules/longestcircuit_ilp.rs index 86b562d99..b0c20b4ad 100644 --- a/src/unit_tests/rules/longestcircuit_ilp.rs +++ b/src/unit_tests/rules/longestcircuit_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -97,10 +97,5 @@ fn test_longestcircuit_to_ilp_bf_vs_ilp() { vec![1, 1, 1], ); let reduction: ReductionLongestCircuitToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/longestcommonsubsequence_ilp.rs b/src/unit_tests/rules/longestcommonsubsequence_ilp.rs index f59044fdd..62ecfcc7a 100644 --- a/src/unit_tests/rules/longestcommonsubsequence_ilp.rs +++ b/src/unit_tests/rules/longestcommonsubsequence_ilp.rs @@ -98,10 +98,5 @@ fn test_lcs_to_ilp_single_position_all_padding() { fn test_longestcommonsubsequence_to_ilp_bf_vs_ilp() { let problem = LongestCommonSubsequence::new(2, vec![vec![0, 1, 0], vec![1, 0, 1]]); let reduction: ReductionLCSToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/longestpath_ilp.rs b/src/unit_tests/rules/longestpath_ilp.rs index 3aae63b88..288d5d172 100644 --- a/src/unit_tests/rules/longestpath_ilp.rs +++ b/src/unit_tests/rules/longestpath_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Max; @@ -111,10 +111,5 @@ fn test_source_equals_target_uses_empty_path() { fn test_longestpath_to_ilp_bf_vs_ilp() { let problem = simple_path_problem(); let reduction: ReductionLongestPathToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/maximumclique_ilp.rs b/src/unit_tests/rules/maximumclique_ilp.rs index ae6639200..21d24c1ae 100644 --- a/src/unit_tests/rules/maximumclique_ilp.rs +++ b/src/unit_tests/rules/maximumclique_ilp.rs @@ -1,6 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; -use crate::traits::Problem; +use crate::solvers::ILPSolver; /// Check if a configuration represents a valid clique in the graph. /// A clique is valid if all selected vertices are pairwise adjacent. @@ -315,10 +314,5 @@ fn test_maximumclique_to_ilp_bf_vs_ilp() { vec![1; 4], ); let reduction: ReductionCliqueToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/maximummatching_ilp.rs b/src/unit_tests/rules/maximummatching_ilp.rs index 37b22b5f2..02c9c6061 100644 --- a/src/unit_tests/rules/maximummatching_ilp.rs +++ b/src/unit_tests/rules/maximummatching_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Max; @@ -254,10 +254,5 @@ fn test_maximummatching_to_ilp_bf_vs_ilp() { let problem = MaximumMatching::<_, i32>::unit_weights(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])); let reduction: ReductionMatchingToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/maximumsetpacking_ilp.rs b/src/unit_tests/rules/maximumsetpacking_ilp.rs index 578a60c49..54daaed04 100644 --- a/src/unit_tests/rules/maximumsetpacking_ilp.rs +++ b/src/unit_tests/rules/maximumsetpacking_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; use crate::types::Max; @@ -132,10 +132,5 @@ fn test_solve_reduced() { fn test_maximumsetpacking_to_ilp_bf_vs_ilp() { let problem = MaximumSetPacking::::new(vec![vec![0, 1], vec![1, 2], vec![2, 3]]); let reduction: ReductionSPToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs b/src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs index dd205b6ab..1dbe73c45 100644 --- a/src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs +++ b/src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs @@ -3,7 +3,6 @@ use crate::models::algebraic::ILP; use crate::models::graph::MinimumCutIntoBoundedSets; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -72,10 +71,5 @@ fn test_larger_instance() { fn test_minimumcutintoboundedsets_to_ilp_bf_vs_ilp() { let source = small_instance(); let reduction: ReductionMinCutBSToILP = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/minimumdominatingset_ilp.rs b/src/unit_tests/rules/minimumdominatingset_ilp.rs index 87ac865ed..42c4f4031 100644 --- a/src/unit_tests/rules/minimumdominatingset_ilp.rs +++ b/src/unit_tests/rules/minimumdominatingset_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; use crate::types::Min; @@ -249,10 +249,5 @@ fn test_minimumdominatingset_to_ilp_bf_vs_ilp() { vec![1i32; 4], ); let reduction: ReductionDSToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs b/src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs index aa678b103..74f12365d 100644 --- a/src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs +++ b/src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::DirectedGraph; use crate::traits::Problem; use crate::types::Min; @@ -199,10 +199,5 @@ fn test_minimumfeedbackvertexset_to_ilp_bf_vs_ilp() { let graph = DirectedGraph::new(3, vec![(0, 1), (1, 2), (2, 0)]); let problem = MinimumFeedbackVertexSet::new(graph, vec![1i32; 3]); let reduction: ReductionMFVSToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/minimummultiwaycut_ilp.rs b/src/unit_tests/rules/minimummultiwaycut_ilp.rs index b974d9033..99260a2ab 100644 --- a/src/unit_tests/rules/minimummultiwaycut_ilp.rs +++ b/src/unit_tests/rules/minimummultiwaycut_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::ObjectiveSense; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Min; @@ -142,10 +142,5 @@ fn test_solve_reduced() { fn test_minimummultiwaycut_to_ilp_bf_vs_ilp() { let problem = canonical_instance(); let reduction: ReductionMMCToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/minimumsetcovering_ilp.rs b/src/unit_tests/rules/minimumsetcovering_ilp.rs index 8382fb92f..cd16428a2 100644 --- a/src/unit_tests/rules/minimumsetcovering_ilp.rs +++ b/src/unit_tests/rules/minimumsetcovering_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; use crate::types::Min; @@ -230,10 +230,5 @@ fn test_constraint_structure() { fn test_minimumsetcovering_to_ilp_bf_vs_ilp() { let problem = MinimumSetCovering::::new(3, vec![vec![0, 1], vec![1, 2], vec![0, 2]]); let reduction: ReductionSCToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/optimallineararrangement_ilp.rs b/src/unit_tests/rules/optimallineararrangement_ilp.rs index 18abb220f..97661c8f1 100644 --- a/src/unit_tests/rules/optimallineararrangement_ilp.rs +++ b/src/unit_tests/rules/optimallineararrangement_ilp.rs @@ -101,10 +101,5 @@ fn test_solution_extraction() { fn test_optimallineararrangement_to_ilp_bf_vs_ilp() { let problem = OptimalLinearArrangement::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])); let reduction: ReductionOLAToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/pathconstrainednetworkflow_ilp.rs b/src/unit_tests/rules/pathconstrainednetworkflow_ilp.rs index afc829c38..a6f971326 100644 --- a/src/unit_tests/rules/pathconstrainednetworkflow_ilp.rs +++ b/src/unit_tests/rules/pathconstrainednetworkflow_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -41,10 +41,5 @@ fn test_pathconstrainednetworkflow_to_ilp_bf_vs_ilp() { 2, ); let reduction = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/precedenceconstrainedscheduling_ilp.rs b/src/unit_tests/rules/precedenceconstrainedscheduling_ilp.rs index 5793a6567..4f4e3a363 100644 --- a/src/unit_tests/rules/precedenceconstrainedscheduling_ilp.rs +++ b/src/unit_tests/rules/precedenceconstrainedscheduling_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; fn feasible_instance() -> PrecedenceConstrainedScheduling { @@ -82,10 +82,5 @@ fn test_precedenceconstrainedscheduling_to_ilp_extract_solution() { fn test_precedenceconstrainedscheduling_to_ilp_bf_vs_ilp() { let problem = feasible_instance(); let reduction: ReductionPCSToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/quadraticassignment_ilp.rs b/src/unit_tests/rules/quadraticassignment_ilp.rs index 0bcf1f270..d7a648d0e 100644 --- a/src/unit_tests/rules/quadraticassignment_ilp.rs +++ b/src/unit_tests/rules/quadraticassignment_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; fn small_qap() -> QuadraticAssignment { @@ -110,10 +110,5 @@ fn test_quadraticassignment_to_ilp_rectangular() { fn test_quadraticassignment_to_ilp_bf_vs_ilp() { let problem = small_qap(); let reduction: ReductionQAPToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/ruralpostman_ilp.rs b/src/unit_tests/rules/ruralpostman_ilp.rs index 61dd83864..8798cd6ea 100644 --- a/src/unit_tests/rules/ruralpostman_ilp.rs +++ b/src/unit_tests/rules/ruralpostman_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -65,10 +65,5 @@ fn test_ruralpostman_to_ilp_bf_vs_ilp() { vec![0], ); let reduction = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/schedulingwithindividualdeadlines_ilp.rs b/src/unit_tests/rules/schedulingwithindividualdeadlines_ilp.rs index 7a86b61f4..569da137e 100644 --- a/src/unit_tests/rules/schedulingwithindividualdeadlines_ilp.rs +++ b/src/unit_tests/rules/schedulingwithindividualdeadlines_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; fn feasible_instance() -> SchedulingWithIndividualDeadlines { @@ -83,10 +83,5 @@ fn test_schedulingwithindividualdeadlines_to_ilp_extract_solution() { fn test_schedulingwithindividualdeadlines_to_ilp_bf_vs_ilp() { let problem = feasible_instance(); let reduction: ReductionSWIDToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/sequencingtominimizeweightedcompletiontime_ilp.rs b/src/unit_tests/rules/sequencingtominimizeweightedcompletiontime_ilp.rs index 96f8653bf..5f599cb07 100644 --- a/src/unit_tests/rules/sequencingtominimizeweightedcompletiontime_ilp.rs +++ b/src/unit_tests/rules/sequencingtominimizeweightedcompletiontime_ilp.rs @@ -1,7 +1,7 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; use crate::models::misc::SequencingToMinimizeWeightedCompletionTime; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; use crate::types::Min; @@ -162,10 +162,5 @@ fn test_solve_reduced_matches_source_optimum() { fn test_sequencingtominimizeweightedcompletiontime_to_ilp_bf_vs_ilp() { let problem = SequencingToMinimizeWeightedCompletionTime::new(vec![2, 1], vec![3, 5], vec![]); let reduction: ReductionSTMWCTToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/sequencingwithinintervals_ilp.rs b/src/unit_tests/rules/sequencingwithinintervals_ilp.rs index 8a4b529de..fa04ef222 100644 --- a/src/unit_tests/rules/sequencingwithinintervals_ilp.rs +++ b/src/unit_tests/rules/sequencingwithinintervals_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::traits::Problem; fn feasible_instance() -> SequencingWithinIntervals { @@ -94,10 +94,5 @@ fn test_sequencingwithinintervals_to_ilp_extract_solution() { fn test_sequencingwithinintervals_to_ilp_bf_vs_ilp() { let problem = feasible_instance(); let reduction: ReductionSWIToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/stackercrane_ilp.rs b/src/unit_tests/rules/stackercrane_ilp.rs index 1cf2aa8bd..d4d6ff22f 100644 --- a/src/unit_tests/rules/stackercrane_ilp.rs +++ b/src/unit_tests/rules/stackercrane_ilp.rs @@ -2,8 +2,6 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; -use crate::traits::Problem; #[test] fn test_stackercrane_to_ilp_closed_loop() { @@ -21,10 +19,5 @@ fn test_stackercrane_to_ilp_closed_loop() { fn test_stackercrane_to_ilp_bf_vs_ilp() { let source = StackerCrane::new(3, vec![(0, 1), (2, 0)], vec![(1, 2)], vec![1, 1], vec![1]); let reduction = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/steinertree_ilp.rs b/src/unit_tests/rules/steinertree_ilp.rs index 87423a563..7925f7ed4 100644 --- a/src/unit_tests/rules/steinertree_ilp.rs +++ b/src/unit_tests/rules/steinertree_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; use crate::models::graph::SteinerTree; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Min; @@ -100,10 +100,5 @@ fn test_reduction_rejects_zero_weights() { fn test_steinertree_to_ilp_bf_vs_ilp() { let problem = canonical_instance(); let reduction: ReductionSteinerTreeToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/steinertreeingraphs_ilp.rs b/src/unit_tests/rules/steinertreeingraphs_ilp.rs index b0bd69cd5..397b9a48c 100644 --- a/src/unit_tests/rules/steinertreeingraphs_ilp.rs +++ b/src/unit_tests/rules/steinertreeingraphs_ilp.rs @@ -2,9 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::rules::test_helpers::assert_optimization_round_trip_from_optimization_target; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; use crate::topology::SimpleGraph; -use crate::traits::Problem; #[test] fn test_steinertreeingraphs_to_ilp_closed_loop() { @@ -32,10 +30,5 @@ fn test_steinertreeingraphs_to_ilp_bf_vs_ilp() { vec![1, 1], ); let reduction = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/strongconnectivityaugmentation_ilp.rs b/src/unit_tests/rules/strongconnectivityaugmentation_ilp.rs index 90fbff51b..924fcc0ec 100644 --- a/src/unit_tests/rules/strongconnectivityaugmentation_ilp.rs +++ b/src/unit_tests/rules/strongconnectivityaugmentation_ilp.rs @@ -2,7 +2,7 @@ use super::*; use crate::models::algebraic::ILP; use crate::models::graph::StrongConnectivityAugmentation; use crate::rules::ReduceTo; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::DirectedGraph; use crate::traits::Problem; @@ -97,10 +97,5 @@ fn test_infeasible_budget() { fn test_strongconnectivityaugmentation_to_ilp_bf_vs_ilp() { let source = small_instance(); let reduction: ReductionSCAToILP = ReduceTo::>::reduce_to(&source); - let bf_value = BruteForce::new().solve(&source); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(source.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction); } diff --git a/src/unit_tests/rules/subgraphisomorphism_ilp.rs b/src/unit_tests/rules/subgraphisomorphism_ilp.rs index 6c80e08a7..a662292f0 100644 --- a/src/unit_tests/rules/subgraphisomorphism_ilp.rs +++ b/src/unit_tests/rules/subgraphisomorphism_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Or; @@ -101,10 +101,5 @@ fn test_subgraphisomorphism_to_ilp_bf_vs_ilp() { let pattern = SimpleGraph::new(3, vec![(0, 1), (1, 2)]); let problem = SubgraphIsomorphism::new(host, pattern); let reduction: ReductionSubIsoToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/travelingsalesman_ilp.rs b/src/unit_tests/rules/travelingsalesman_ilp.rs index 7afdd6faf..cb0040030 100644 --- a/src/unit_tests/rules/travelingsalesman_ilp.rs +++ b/src/unit_tests/rules/travelingsalesman_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; use crate::types::Min; @@ -155,10 +155,5 @@ fn test_travelingsalesman_to_ilp_bf_vs_ilp() { vec![(0, 1), (1, 2), (2, 3), (3, 0)], )); let reduction: ReductionTSPToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/undirectedflowlowerbounds_ilp.rs b/src/unit_tests/rules/undirectedflowlowerbounds_ilp.rs index 2003c2d57..2969919dd 100644 --- a/src/unit_tests/rules/undirectedflowlowerbounds_ilp.rs +++ b/src/unit_tests/rules/undirectedflowlowerbounds_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -99,10 +99,5 @@ fn test_undirectedflowlowerbounds_to_ilp_extract_solution() { fn test_undirectedflowlowerbounds_to_ilp_bf_vs_ilp() { let problem = feasible_instance(); let reduction: ReductionUFLBToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } diff --git a/src/unit_tests/rules/undirectedtwocommodityintegralflow_ilp.rs b/src/unit_tests/rules/undirectedtwocommodityintegralflow_ilp.rs index 2ce7bf76f..398f8c485 100644 --- a/src/unit_tests/rules/undirectedtwocommodityintegralflow_ilp.rs +++ b/src/unit_tests/rules/undirectedtwocommodityintegralflow_ilp.rs @@ -1,6 +1,6 @@ use super::*; use crate::models::algebraic::{ObjectiveSense, ILP}; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -134,10 +134,5 @@ fn test_undirectedtwocommodityintegralflow_to_ilp_extract_solution() { fn test_undirectedtwocommodityintegralflow_to_ilp_bf_vs_ilp() { let problem = feasible_instance(); let reduction: ReductionU2CIFToILP = ReduceTo::>::reduce_to(&problem); - let bf_value = BruteForce::new().solve(&problem); - let ilp_solution = ILPSolver::new() - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - assert_eq!(problem.evaluate(&extracted), bf_value); + crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction); } From b9980762ffc8e3a8678ef07448b18c18fcbd9a9f Mon Sep 17 00:00:00 2001 From: Xiwei Pan Date: Thu, 26 Mar 2026 23:01:41 +0800 Subject: [PATCH 3/3] 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) --- .../rules/maximumindependentset_ilp.rs | 4 ---- .../rules/minimumvertexcover_ilp.rs | 4 ---- .../rules/optimallineararrangement_ilp.rs | 24 +------------------ 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/src/unit_tests/rules/maximumindependentset_ilp.rs b/src/unit_tests/rules/maximumindependentset_ilp.rs index d3361d850..ce3c165c5 100644 --- a/src/unit_tests/rules/maximumindependentset_ilp.rs +++ b/src/unit_tests/rules/maximumindependentset_ilp.rs @@ -62,15 +62,11 @@ fn test_maximumindependentset_to_ilp_via_path_closed_loop() { let (_, chain) = reduce_mis_to_ilp(&problem); let ilp: &ILP = chain.target_problem(); - let bf = BruteForce::new(); let ilp_solver = ILPSolver::new(); - let bf_solutions = bf.find_all_witnesses(&problem); let ilp_solution = ilp_solver.solve(ilp).expect("ILP should be solvable"); let extracted = chain.extract_solution(&ilp_solution); - let bf_size: usize = bf_solutions[0].iter().sum(); let ilp_size: usize = extracted.iter().sum(); - assert_eq!(bf_size, 2); assert_eq!(ilp_size, 2); assert!(problem.evaluate(&extracted).is_valid()); } diff --git a/src/unit_tests/rules/minimumvertexcover_ilp.rs b/src/unit_tests/rules/minimumvertexcover_ilp.rs index 119d5fa86..736072a62 100644 --- a/src/unit_tests/rules/minimumvertexcover_ilp.rs +++ b/src/unit_tests/rules/minimumvertexcover_ilp.rs @@ -59,15 +59,11 @@ fn test_minimumvertexcover_to_ilp_via_path_closed_loop() { let (_, chain) = reduce_vc_to_ilp(&problem); let ilp: &ILP = chain.target_problem(); - let bf = BruteForce::new(); let ilp_solver = ILPSolver::new(); - let bf_solutions = bf.find_all_witnesses(&problem); let ilp_solution = ilp_solver.solve(ilp).expect("ILP should be solvable"); let extracted = chain.extract_solution(&ilp_solution); - let bf_size: usize = bf_solutions[0].iter().sum(); let ilp_size: usize = extracted.iter().sum(); - assert_eq!(bf_size, 2); assert_eq!(ilp_size, 2); assert!(problem.evaluate(&extracted).is_valid()); } diff --git a/src/unit_tests/rules/optimallineararrangement_ilp.rs b/src/unit_tests/rules/optimallineararrangement_ilp.rs index 97661c8f1..661b50569 100644 --- a/src/unit_tests/rules/optimallineararrangement_ilp.rs +++ b/src/unit_tests/rules/optimallineararrangement_ilp.rs @@ -1,5 +1,5 @@ use super::*; -use crate::solvers::{BruteForce, ILPSolver, Solver}; +use crate::solvers::{BruteForce, ILPSolver}; use crate::topology::SimpleGraph; use crate::traits::Problem; @@ -63,28 +63,6 @@ fn test_optimallineararrangement_to_ilp_with_chords() { assert!(problem.evaluate(&extracted).0.is_some()); } -#[test] -fn test_optimallineararrangement_to_ilp_optimization() { - // Path P4: optimal cost is 3 - let problem = OptimalLinearArrangement::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)])); - let reduction: ReductionOLAToILP = ReduceTo::>::reduce_to(&problem); - - // Cannot brute-force ILP (integer domain too large), so compare BF source vs ILP solver - let bf = BruteForce::new(); - let bf_value = bf.solve(&problem); - - let ilp_solver = ILPSolver::new(); - let ilp_solution = ilp_solver - .solve(reduction.target_problem()) - .expect("ILP should be solvable"); - let extracted = reduction.extract_solution(&ilp_solution); - let ilp_value = problem.evaluate(&extracted); - assert_eq!( - bf_value, ilp_value, - "BF and ILP should agree on optimal value" - ); -} - #[test] fn test_solution_extraction() { let problem = OptimalLinearArrangement::new(SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]));