Skip to content

Commit b98d3b0

Browse files
GiggleLiuclaude
andauthored
Fix #762: Add 24 Tier 2 ILP reductions + backfill tests (#763)
* feat: add MinimumHittingSet → ILP reduction Implements the MinimumHittingSet to ILP reduction with: - Binary variables x_e per universe element - Constraints ∀ set S: Σ_{e∈S} x_e ≥ 1 - Objective: minimize Σ x_e (unit weight) - Direct 1:1 solution extraction - Overhead: num_vars = universe_size, num_constraints = num_sets Includes 4 test cases covering structure, closed-loop, extraction, and trivial instances. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add MaximalIS → ILP reduction Implements ILP formulation for MaximalIS with independence constraints (x_u + x_v ≤ 1 per edge) and maximality constraints (x_v + Σ_{u∈N(v)} x_u ≥ 1 per vertex), maximizing the weighted sum. Includes 6 unit tests covering structure, BF-vs-ILP parity, solution extraction, trivial, star, weighted, and path instances. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add PartiallyOrderedKnapsack → ILP reduction Implements the binary ILP formulation: one variable per item, a capacity constraint, precedence constraints (x_b ≤ x_a for each (a,b) pair), and a Maximize objective over item values. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add KClique → ILP reduction Binary ILP formulation: x_v per vertex, cardinality constraint Σ x_v ≥ k, non-edge constraints x_u + x_v ≤ 1, empty objective (feasibility). Four tests covering ILP structure, BF vs ILP parity, solution extraction, and trivial case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add RectilinearPictureCompression → ILP reduction Binary ILP with one variable per maximal rectangle, coverage constraints for each 1-cell, and a bound constraint. Feasibility (Or) objective. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add ExactCoverBy3Sets → ILP reduction Binary variable x_j per 3-element subset; equality constraints enforce exact cover (each element covered once) plus a cardinality constraint (universe_size/3 triples selected); empty objective for pure feasibility. Also fix compilation errors in sibling ILP rules on this branch (from_edges → new, missing Graph import, deref pattern fix). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove unused Max import from maximalis_ilp test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add Batch 1 ILP reductions (6 rules) MinimumHittingSet, ExactCoverBy3Sets, KClique, MaximalIS, PartiallyOrderedKnapsack, RectilinearPictureCompression → ILP Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add MinimumFeedbackArcSet → ILP reduction Implements MTZ-style topological ordering constraints to reduce MinimumFeedbackArcSet<i32> to ILP<i32>: binary arc-removal variables plus integer ordering variables, with arc-ordering, binary-bound, and order-bound constraints. Includes 4 unit tests covering structure, bf-vs-ilp comparison, solution extraction, and trivial DAG case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add MultipleCopyFileAllocation → ILP reduction Binary x_v / y_{v,u} formulation with assignment, capacity-link, and budget constraints; BFS all-pairs distances handle unreachable pairs via big-M; four closed-loop tests verify structure, BF-vs-ILP, extraction, and trivial single-vertex instance. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add NAESatisfiability → ILP reduction Implements the NAE-SAT to ILP reduction using two constraints per clause: a lower bound (at least one true literal) and an upper bound (at least one false literal), using the standard signed-literal encoding. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add Batch 2 ILP reductions (NAESatisfiability, SWCP, MCFA) NAESatisfiability → ILP<bool>: binary per variable, NAE constraints ShortestWeightConstrainedPath → ILP<i32>: directed arc + MTZ ordering MultipleCopyFileAllocation → ILP<bool>: placement + assignment + BFS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style: fix rustfmt formatting in SWCP ILP test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add MinimumSumMulticenter → ILP reduction Implements the p-median binary ILP formulation with binary center variables x_j, assignment variables y_{i,j}, cardinality/assignment/capacity constraints, and a weighted-distance minimization objective using BFS all-pairs distances. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add MinMaxMulticenter → ILP reduction Implements the vertex p-center binary ILP formulation with binary center variables x_j, assignment variables y_{i,j}, cardinality/assignment/capacity/bound constraints, and a feasibility objective using BFS all-pairs distances. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add MultiprocessorScheduling → ILP reduction Binary one-hot formulation with task assignment and processor load constraints. Includes 4 unit tests covering creation, BF vs ILP, extraction, and trivial case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add CapacityAssignment → ILP reduction Binary one-hot formulation with per-link assignment and global cost/delay budget constraints. Includes 4 unit tests covering creation, BF vs ILP, extraction, and trivial case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add ExpectedRetrievalCost → ILP reduction One-hot assignment with McCormick linearization of the quadratic expected-cost objective. Includes 4 unit tests covering structure, BF vs ILP, extraction, and trivial case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add PartitionIntoTriangles → ILP reduction One-hot group assignment with size-3 constraints and non-edge exclusion per group. Includes 4 unit tests covering structure, BF vs ILP, extraction, and trivial case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add PartitionIntoPathsOfLength2 → ILP reduction One-hot assignment with McCormick-linearized edge-count constraint requiring ≥2 edges per group. Includes 4 unit tests covering structure, BF vs ILP, extraction, and trivial case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add SumOfSquaresPartition → ILP reduction One-hot assignment with McCormick linearization of the quadratic sum-of-squares bound. Includes 4 unit tests covering structure, BF vs ILP, extraction, and trivial case. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: register 6 new ILP reduction rules in mod.rs Wire capacityassignment, expectedretrievalcost, multiprocessorscheduling, partitionintopathsoflength2, partitionintotriangles, and sumofsquarespartition ILP rules into the module tree and example-db registry. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add PrecedenceConstrainedScheduling → ILP reduction Time-indexed binary ILP with one-hot per task, processor capacity, and precedence constraints. 4 tests covering structure, closed-loop, infeasibility, and extraction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add SchedulingWithIndividualDeadlines → ILP reduction Time-indexed binary ILP with per-task deadline windows, processor capacity, and precedence constraints. 4 tests covering structure, closed-loop, infeasibility, and extraction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add SequencingWithinIntervals → ILP reduction Time-indexed binary ILP with per-task variable-width start windows and pairwise non-overlap constraints between overlapping task pairs. 4 tests covering structure, closed-loop, infeasibility, and extraction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add UndirectedTwoCommodityIntegralFlow → ILP reduction Integer ILP with 8 variables per edge: 4 directional flow variables plus 2 binary direction indicators per commodity. Joint capacity enforced via big-M linearization. 4 tests covering structure, closed-loop, infeasibility, and extraction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add DirectedTwoCommodityIntegralFlow → ILP reduction Integer ILP with 2|A| variables (one per commodity per arc). Joint arc capacity, flow conservation at non-terminals, and sink requirements. 4 tests covering structure, closed-loop, infeasibility, and extraction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add UndirectedFlowLowerBounds → ILP reduction Integer ILP with 3|E| variables: bidirectional flows plus binary orientation indicators. Big-M constraints enforce lower and upper capacity bounds per chosen direction. Extraction inverts orientation indicator to match model convention. 4 tests covering structure, closed-loop, infeasibility, and extraction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: complete Tier 2 ILP reductions (Batches 3-6), paper stubs, and backfill tests Add remaining 18 ILP reduction rules (Batches 3-5), backfill BF-vs-ILP comparison tests for 4 existing rules (Batch 6), add 24 paper stubs in reductions.typ, fix clippy warnings, canonical example consistency, and wire all example_db entries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix ci --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b0f2cab commit b98d3b0

57 files changed

Lines changed: 6300 additions & 41 deletions

File tree

Some content is hidden

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

docs/paper/reductions.typ

Lines changed: 240 additions & 0 deletions
Large diffs are not rendered by default.

examples/export_module_graph.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,11 @@ fn main() {
110110
(
111111
"traits",
112112
"core",
113-
&[
114-
(
115-
"Problem",
116-
"trait",
117-
"Core trait for all computational problems",
118-
),
119-
],
113+
&[(
114+
"Problem",
115+
"trait",
116+
"Core trait for all computational problems",
117+
)],
120118
),
121119
(
122120
"types",
@@ -160,11 +158,7 @@ fn main() {
160158
"rules",
161159
"rule",
162160
&[
163-
(
164-
"ReduceTo",
165-
"trait",
166-
"Trait for witness/config reductions",
167-
),
161+
("ReduceTo", "trait", "Trait for witness/config reductions"),
168162
(
169163
"ReductionResult",
170164
"trait",

problemreductions-cli/tests/cli_tests.rs

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,7 +1519,7 @@ fn test_create_d2cif_alias() {
15191519
}
15201520

15211521
#[test]
1522-
fn test_solve_d2cif_default_solver_suggests_bruteforce() {
1522+
fn test_solve_d2cif_default_solver_uses_ilp() {
15231523
let output_file = std::env::temp_dir().join("pred_test_solve_d2cif.json");
15241524
let create_output = pred()
15251525
.args([
@@ -1557,21 +1557,25 @@ fn test_solve_d2cif_default_solver_suggests_bruteforce() {
15571557
.output()
15581558
.unwrap();
15591559
assert!(
1560-
!solve_output.status.success(),
1561-
"stdout: {}",
1562-
String::from_utf8_lossy(&solve_output.stdout)
1560+
solve_output.status.success(),
1561+
"stderr: {}",
1562+
String::from_utf8_lossy(&solve_output.stderr)
15631563
);
1564-
let stderr = String::from_utf8_lossy(&solve_output.stderr);
1564+
let stdout = String::from_utf8(solve_output.stdout).unwrap();
15651565
assert!(
1566-
stderr.contains("--solver brute-force"),
1567-
"expected brute-force hint, got: {stderr}"
1566+
stdout.contains("\"solver\": \"ilp\""),
1567+
"expected ILP solver output, got: {stdout}"
1568+
);
1569+
assert!(
1570+
stdout.contains("\"reduced_to\": \"ILP\""),
1571+
"expected auto-reduction marker, got: {stdout}"
15681572
);
15691573

15701574
std::fs::remove_file(&output_file).ok();
15711575
}
15721576

15731577
#[test]
1574-
fn test_inspect_rectilinear_picture_compression_lists_bruteforce_only() {
1578+
fn test_inspect_rectilinear_picture_compression_lists_ilp_and_bruteforce() {
15751579
let output_file = std::env::temp_dir().join("pred_test_inspect_rpc.json");
15761580
let create_output = pred()
15771581
.args([
@@ -1604,8 +1608,8 @@ fn test_inspect_rectilinear_picture_compression_lists_bruteforce_only() {
16041608
let stdout = String::from_utf8(inspect_output.stdout).unwrap();
16051609
let json: serde_json::Value = serde_json::from_str(&stdout).unwrap();
16061610
assert!(
1607-
json["solvers"] == serde_json::json!(["brute-force"]),
1608-
"inspect should list only usable solvers, got: {json}"
1611+
json["solvers"] == serde_json::json!(["ilp", "brute-force"]),
1612+
"inspect should list ILP first when available, got: {json}"
16091613
);
16101614

16111615
std::fs::remove_file(&output_file).ok();
@@ -4422,7 +4426,7 @@ fn test_create_minmaxmulticenter_negative_inputs_rejected() {
44224426
}
44234427

44244428
#[test]
4425-
fn test_solve_minmaxmulticenter_ilp_error_suggests_bruteforce() {
4429+
fn test_solve_minmaxmulticenter_default_solver_uses_ilp() {
44264430
let problem_file = std::env::temp_dir().join("pred_test_minmaxmulticenter_solve.json");
44274431
let create_out = pred()
44284432
.args([
@@ -4449,9 +4453,17 @@ fn test_solve_minmaxmulticenter_ilp_error_suggests_bruteforce() {
44494453
.args(["solve", problem_file.to_str().unwrap()])
44504454
.output()
44514455
.unwrap();
4452-
assert!(!solve_out.status.success());
4453-
let stderr = String::from_utf8_lossy(&solve_out.stderr);
4454-
assert!(stderr.contains("--solver brute-force"), "stderr: {stderr}");
4456+
assert!(
4457+
solve_out.status.success(),
4458+
"stderr: {}",
4459+
String::from_utf8_lossy(&solve_out.stderr)
4460+
);
4461+
let stdout = String::from_utf8(solve_out.stdout).unwrap();
4462+
assert!(stdout.contains("\"solver\": \"ilp\""), "stdout: {stdout}");
4463+
assert!(
4464+
stdout.contains("\"reduced_to\": \"ILP\""),
4465+
"stdout: {stdout}"
4466+
);
44554467

44564468
std::fs::remove_file(&problem_file).ok();
44574469
}
@@ -5644,7 +5656,7 @@ fn test_create_pipe_to_solve() {
56445656
}
56455657

56465658
#[test]
5647-
fn test_solve_ilp_error_suggests_brute_force_fallback() {
5659+
fn test_solve_sum_of_squares_partition_default_solver_uses_ilp() {
56485660
let problem_json = r#"{
56495661
"type": "SumOfSquaresPartition",
56505662
"data": {
@@ -5660,12 +5672,20 @@ fn test_solve_ilp_error_suggests_brute_force_fallback() {
56605672
.args(["solve", tmp.to_str().unwrap()])
56615673
.output()
56625674
.unwrap();
5663-
assert!(!output.status.success());
5675+
assert!(
5676+
output.status.success(),
5677+
"stderr: {}",
5678+
String::from_utf8_lossy(&output.stderr)
5679+
);
56645680

5665-
let stderr = String::from_utf8_lossy(&output.stderr);
5681+
let stdout = String::from_utf8(output.stdout).unwrap();
56665682
assert!(
5667-
stderr.contains("--solver brute-force"),
5668-
"stderr should suggest the brute-force fallback, got: {stderr}"
5683+
stdout.contains("\"solver\": \"ilp\""),
5684+
"stdout should report the ILP solver, got: {stdout}"
5685+
);
5686+
assert!(
5687+
stdout.contains("\"reduced_to\": \"ILP\""),
5688+
"stdout should report the ILP reduction target, got: {stdout}"
56695689
);
56705690

56715691
std::fs::remove_file(&tmp).ok();
@@ -5848,7 +5868,7 @@ fn test_inspect_problem() {
58485868
}
58495869

58505870
#[test]
5851-
fn test_inspect_minmaxmulticenter_lists_bruteforce_only() {
5871+
fn test_inspect_minmaxmulticenter_lists_ilp_and_bruteforce() {
58525872
let problem_file = std::env::temp_dir().join("pred_test_inspect_minmaxmulticenter.json");
58535873
let create_out = pred()
58545874
.args([
@@ -5888,7 +5908,7 @@ fn test_inspect_minmaxmulticenter_lists_bruteforce_only() {
58885908
.iter()
58895909
.map(|v| v.as_str().unwrap())
58905910
.collect();
5891-
assert_eq!(solvers, vec!["brute-force"]);
5911+
assert_eq!(solvers, vec!["ilp", "brute-force"]);
58925912

58935913
std::fs::remove_file(&problem_file).ok();
58945914
}
@@ -6094,7 +6114,7 @@ fn test_inspect_json_output() {
60946114
}
60956115

60966116
#[test]
6097-
fn test_inspect_multiprocessor_scheduling_reports_only_brute_force_solver() {
6117+
fn test_inspect_multiprocessor_scheduling_reports_ilp_and_brute_force() {
60986118
let problem_file = std::env::temp_dir().join("pred_test_inspect_mps_in.json");
60996119
let result_file = std::env::temp_dir().join("pred_test_inspect_mps_out.json");
61006120
let create_out = pred()
@@ -6143,7 +6163,7 @@ fn test_inspect_multiprocessor_scheduling_reports_only_brute_force_solver() {
61436163
.collect();
61446164
assert_eq!(
61456165
solvers,
6146-
vec!["brute-force"],
6166+
vec!["ilp", "brute-force"],
61476167
"unexpected solvers: {solvers:?}"
61486168
);
61496169

@@ -6381,7 +6401,7 @@ fn test_inspect_multiple_copy_file_allocation_reports_size_fields() {
63816401
.iter()
63826402
.map(|v| v.as_str().unwrap())
63836403
.collect();
6384-
assert_eq!(solvers, vec!["brute-force"]);
6404+
assert_eq!(solvers, vec!["ilp", "brute-force"]);
63856405

63866406
std::fs::remove_file(&problem_file).ok();
63876407
std::fs::remove_file(&result_file).ok();

src/models/graph/undirected_two_commodity_integral_flow.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ inventory::submit! {
3232
inventory::submit! {
3333
ProblemSizeFieldEntry {
3434
name: "UndirectedTwoCommodityIntegralFlow",
35-
fields: &["num_vertices", "num_edges"],
35+
fields: &["num_vertices", "num_edges", "num_nonterminal_vertices"],
3636
}
3737
}
3838

@@ -149,6 +149,12 @@ impl UndirectedTwoCommodityIntegralFlow {
149149
self.graph.num_edges()
150150
}
151151

152+
pub fn num_nonterminal_vertices(&self) -> usize {
153+
(0..self.num_vertices())
154+
.filter(|&vertex| !self.is_terminal(vertex))
155+
.count()
156+
}
157+
152158
pub fn is_valid_solution(&self, config: &[usize]) -> bool {
153159
self.evaluate(config).0
154160
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//! Reduction from CapacityAssignment to ILP (Integer Linear Programming).
2+
//!
3+
//! The Capacity Assignment feasibility problem can be formulated as a binary ILP:
4+
//! - Variables: Binary x_{l,c} (link l gets capacity c), one-hot per link
5+
//! - Constraints: Σ_c x_{l,c} = 1 for each link l (assignment);
6+
//! Σ_{l,c} cost[l][c]·x_{l,c} ≤ cost_budget; Σ_{l,c} delay[l][c]·x_{l,c} ≤ delay_budget
7+
//! - Objective: Minimize 0 (feasibility)
8+
//! - Extraction: argmax_c x_{l,c} for each link l
9+
10+
use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
11+
use crate::models::misc::CapacityAssignment;
12+
use crate::reduction;
13+
use crate::rules::traits::{ReduceTo, ReductionResult};
14+
15+
/// Result of reducing CapacityAssignment to ILP.
16+
///
17+
/// Variable layout: x_{l,c} at index l * num_capacities + c.
18+
/// - l ∈ 0..num_links, c ∈ 0..num_capacities
19+
///
20+
/// Total: num_links * num_capacities variables.
21+
#[derive(Debug, Clone)]
22+
pub struct ReductionCAToILP {
23+
target: ILP<bool>,
24+
num_links: usize,
25+
num_capacities: usize,
26+
}
27+
28+
impl ReductionResult for ReductionCAToILP {
29+
type Source = CapacityAssignment;
30+
type Target = ILP<bool>;
31+
32+
fn target_problem(&self) -> &ILP<bool> {
33+
&self.target
34+
}
35+
36+
/// Extract solution: for each link l, find the unique capacity c where x_{l,c} = 1.
37+
fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
38+
let num_capacities = self.num_capacities;
39+
(0..self.num_links)
40+
.map(|l| {
41+
(0..num_capacities)
42+
.find(|&c| target_solution[l * num_capacities + c] == 1)
43+
.unwrap_or(0)
44+
})
45+
.collect()
46+
}
47+
}
48+
49+
#[reduction(
50+
overhead = {
51+
num_vars = "num_links * num_capacities",
52+
num_constraints = "num_links + 2",
53+
}
54+
)]
55+
impl ReduceTo<ILP<bool>> for CapacityAssignment {
56+
type Result = ReductionCAToILP;
57+
58+
fn reduce_to(&self) -> Self::Result {
59+
let num_links = self.num_links();
60+
let num_capacities = self.num_capacities();
61+
let num_vars = num_links * num_capacities;
62+
63+
let mut constraints = Vec::with_capacity(num_links + 2);
64+
65+
// Assignment constraints: for each link l, Σ_c x_{l,c} = 1
66+
for l in 0..num_links {
67+
let terms: Vec<(usize, f64)> = (0..num_capacities)
68+
.map(|c| (l * num_capacities + c, 1.0))
69+
.collect();
70+
constraints.push(LinearConstraint::eq(terms, 1.0));
71+
}
72+
73+
// Cost budget constraint: Σ_{l,c} cost[l][c] * x_{l,c} ≤ cost_budget
74+
let cost_terms: Vec<(usize, f64)> = (0..num_links)
75+
.flat_map(|l| {
76+
(0..num_capacities).map(move |c| (l * num_capacities + c, self.cost()[l][c] as f64))
77+
})
78+
.collect();
79+
constraints.push(LinearConstraint::le(cost_terms, self.cost_budget() as f64));
80+
81+
// Delay budget constraint: Σ_{l,c} delay[l][c] * x_{l,c} ≤ delay_budget
82+
let delay_terms: Vec<(usize, f64)> = (0..num_links)
83+
.flat_map(|l| {
84+
(0..num_capacities)
85+
.map(move |c| (l * num_capacities + c, self.delay()[l][c] as f64))
86+
})
87+
.collect();
88+
constraints.push(LinearConstraint::le(
89+
delay_terms,
90+
self.delay_budget() as f64,
91+
));
92+
93+
let target = ILP::new(num_vars, constraints, vec![], ObjectiveSense::Minimize);
94+
95+
ReductionCAToILP {
96+
target,
97+
num_links,
98+
num_capacities,
99+
}
100+
}
101+
}
102+
103+
#[cfg(feature = "example-db")]
104+
pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
105+
use crate::export::SolutionPair;
106+
107+
vec![crate::example_db::specs::RuleExampleSpec {
108+
id: "capacityassignment_to_ilp",
109+
build: || {
110+
// 2 links, 2 capacity levels
111+
// cost: [[1,3],[2,4]], delay: [[8,4],[7,3]]
112+
// budgets: cost=5, delay=12
113+
// Solution: link 0 → cap 0 (cost=1, delay=8), link 1 → cap 0 (cost=2, delay=7)
114+
// total cost=3 ≤ 5, total delay=15 > 12 -- try cap 1 for both
115+
// link 0 → cap 1 (cost=3, delay=4), link 1 → cap 1 (cost=4, delay=3)
116+
// total cost=7 > 5 -- try mixed: link 0 → cap 0, link 1 → cap 0: cost=3≤5, delay=15>12
117+
// link 0 → cap 1 (cost=3, delay=4), link 1 → cap 0 (cost=2, delay=7): cost=5≤5, delay=11≤12
118+
let source = CapacityAssignment::new(
119+
vec![1, 2],
120+
vec![vec![1, 3], vec![2, 4]],
121+
vec![vec![8, 4], vec![7, 3]],
122+
5,
123+
12,
124+
);
125+
crate::example_db::specs::rule_example_with_witness::<_, ILP<bool>>(
126+
source,
127+
SolutionPair {
128+
// link 0 → cap 1, link 1 → cap 0
129+
source_config: vec![1, 0],
130+
// x_{0,0}=0, x_{0,1}=1, x_{1,0}=1, x_{1,1}=0
131+
target_config: vec![0, 1, 1, 0],
132+
},
133+
)
134+
},
135+
}]
136+
}
137+
138+
#[cfg(test)]
139+
#[path = "../unit_tests/rules/capacityassignment_ilp.rs"]
140+
mod tests;

0 commit comments

Comments
 (0)