Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/rules/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,23 @@ pub(crate) fn assert_satisfaction_round_trip_from_satisfaction_target<R>(
);
}

#[cfg(feature = "ilp-solver")]
pub(crate) fn assert_bf_vs_ilp<R>(source: &R::Source, reduction: &R)
where
R: ReductionResult,
R::Source: Problem + 'static,
R::Target: 'static,
<R::Source as Problem>::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<P>(problem: &P) -> Option<Vec<usize>>
where
P: Problem + 'static,
Expand Down
7 changes: 7 additions & 0 deletions src/unit_tests/rules/acyclicpartition_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,10 @@ 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::<ILP<i32>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/balancedcompletebipartitesubgraph_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,10 @@ 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::<ILP<bool>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/bicliquecover_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,10 @@ 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::<ILP<bool>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/biconnectivityaugmentation_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,10 @@ 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::<ILP<i32>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/binpacking_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,10 @@ 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::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/bottlenecktravelingsalesman_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,10 @@ 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::<ILP<i32>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/boundedcomponentspanningforest_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,10 @@ 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::<ILP<i32>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
12 changes: 12 additions & 0 deletions src/unit_tests/rules/capacityassignment_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,15 @@ 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::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/coloring_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,10 @@ 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::<K3, _>::new(SimpleGraph::new(3, vec![(0, 1), (1, 2), (0, 2)]));
let reduction = ReduceTo::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/directedtwocommodityintegralflow_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,10 @@ 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::<ILP<i32>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
10 changes: 10 additions & 0 deletions src/unit_tests/rules/disjointconnectingpaths_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ 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::<ILP<bool>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/factoring_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,10 @@ 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::<ILP<i32>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/hamiltonianpath_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ 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::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}

#[test]
fn test_hamiltonianpath_to_ilp_no_path() {
// Disconnected graph: no Hamiltonian path
Expand Down
7 changes: 7 additions & 0 deletions src/unit_tests/rules/integralflowbundles_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,10 @@ 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::<ILP<i32>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
14 changes: 14 additions & 0 deletions src/unit_tests/rules/integralflowhomologousarcs_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,17 @@ 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::<ILP<i32>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
14 changes: 14 additions & 0 deletions src/unit_tests/rules/integralflowwithmultipliers_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,17 @@ 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::<ILP<i32>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
12 changes: 12 additions & 0 deletions src/unit_tests/rules/lengthboundeddisjointpaths_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,15 @@ 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::<ILP<bool>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
10 changes: 10 additions & 0 deletions src/unit_tests/rules/longestcircuit_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,13 @@ 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::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/longestcommonsubsequence_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,10 @@ 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::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/longestpath_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,10 @@ 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::<ILP<i32>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
10 changes: 10 additions & 0 deletions src/unit_tests/rules/maximumclique_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,13 @@ 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<SimpleGraph, i32> = MaximumClique::new(
SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]),
vec![1; 4],
);
let reduction: ReductionCliqueToILP = ReduceTo::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
20 changes: 15 additions & 5 deletions src/unit_tests/rules/maximumindependentset_ilp.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -62,15 +62,11 @@ fn test_maximumindependentset_to_ilp_via_path_closed_loop() {
let (_, chain) = reduce_mis_to_ilp(&problem);
let ilp: &ILP<bool> = 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());
}
Expand All @@ -89,3 +85,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<bool> = 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);
}
8 changes: 8 additions & 0 deletions src/unit_tests/rules/maximummatching_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,3 +248,11 @@ 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::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/maximumsetpacking_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,10 @@ 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::<i32>::new(vec![vec![0, 1], vec![1, 2], vec![2, 3]]);
let reduction: ReductionSPToILP = ReduceTo::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
7 changes: 7 additions & 0 deletions src/unit_tests/rules/minimumcutintoboundedsets_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,10 @@ fn test_larger_instance() {
"MinCutBS larger instance",
);
}

#[test]
fn test_minimumcutintoboundedsets_to_ilp_bf_vs_ilp() {
let source = small_instance();
let reduction: ReductionMinCutBSToILP = ReduceTo::<ILP<bool>>::reduce_to(&source);
crate::rules::test_helpers::assert_bf_vs_ilp(&source, &reduction);
}
10 changes: 10 additions & 0 deletions src/unit_tests/rules/minimumdominatingset_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,13 @@ 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::<ILP<bool>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
8 changes: 8 additions & 0 deletions src/unit_tests/rules/minimumfeedbackvertexset_ilp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,11 @@ 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::<ILP<i32>>::reduce_to(&problem);
crate::rules::test_helpers::assert_bf_vs_ilp(&problem, &reduction);
}
Loading
Loading