problemreductions/rules/
multiplecopyfileallocation_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
18use crate::models::graph::MultipleCopyFileAllocation;
19use crate::reduction;
20use crate::rules::traits::{ReduceTo, ReductionResult};
21use crate::topology::{Graph, SimpleGraph};
22use std::collections::VecDeque;
23
24#[derive(Debug, Clone)]
26pub struct ReductionMCFAToILP {
27 target: ILP<bool>,
28 num_vertices: usize,
29}
30
31impl ReductionResult for ReductionMCFAToILP {
32 type Source = MultipleCopyFileAllocation;
33 type Target = ILP<bool>;
34
35 fn target_problem(&self) -> &ILP<bool> {
36 &self.target
37 }
38
39 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
40 target_solution[..self.num_vertices].to_vec()
41 }
42}
43
44fn bfs_distances(graph: &SimpleGraph, source: usize, n: usize) -> Vec<i64> {
48 let mut dist = vec![-1i64; n];
49 dist[source] = 0;
50 let mut queue = VecDeque::new();
51 queue.push_back(source);
52 while let Some(u) = queue.pop_front() {
53 for v in graph.neighbors(u) {
54 if dist[v] == -1 {
55 dist[v] = dist[u] + 1;
56 queue.push_back(v);
57 }
58 }
59 }
60 dist
61}
62
63#[reduction(
64 overhead = {
65 num_vars = "num_vertices + num_vertices^2",
66 num_constraints = "num_vertices^2 + num_vertices",
67 }
68)]
69impl ReduceTo<ILP<bool>> for MultipleCopyFileAllocation {
70 type Result = ReductionMCFAToILP;
71
72 fn reduce_to(&self) -> Self::Result {
73 let n = self.num_vertices();
74 let num_vars = n + n * n;
75 let total_storage: i64 = self.storage().iter().sum();
78 let total_usage: i64 = self.usage().iter().sum();
79 let big_m = total_storage + total_usage * n as i64 + 1;
80
81 let all_dist: Vec<Vec<i64>> = (0..n).map(|s| bfs_distances(self.graph(), s, n)).collect();
83
84 let eff_dist = |v: usize, u: usize| -> i64 {
86 let d = all_dist[u][v]; if d < 0 {
88 big_m
89 } else {
90 d
91 }
92 };
93
94 let x_var = |v: usize| v;
96 let y_var = |v: usize, u: usize| n + v * n + u;
97
98 let mut constraints = Vec::with_capacity(n * n + n);
99
100 for v in 0..n {
102 let terms: Vec<(usize, f64)> = (0..n).map(|u| (y_var(v, u), 1.0)).collect();
103 constraints.push(LinearConstraint::eq(terms, 1.0));
104 }
105
106 for v in 0..n {
108 for u in 0..n {
109 constraints.push(LinearConstraint::le(
110 vec![(y_var(v, u), 1.0), (x_var(u), -1.0)],
111 0.0,
112 ));
113 }
114 }
115
116 let mut objective: Vec<(usize, f64)> = Vec::with_capacity(num_vars);
118 for v in 0..n {
119 let sc = self.storage()[v] as f64;
120 if sc != 0.0 {
121 objective.push((x_var(v), sc));
122 }
123 }
124 for v in 0..n {
125 let u_v = self.usage()[v] as f64;
126 for u in 0..n {
127 let coeff = u_v * eff_dist(v, u) as f64;
128 if coeff != 0.0 {
129 objective.push((y_var(v, u), coeff));
130 }
131 }
132 }
133
134 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize);
135 ReductionMCFAToILP {
136 target,
137 num_vertices: n,
138 }
139 }
140}
141
142#[cfg(feature = "example-db")]
143pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
144 vec![crate::example_db::specs::RuleExampleSpec {
145 id: "multiplecopyfileallocation_to_ilp",
146 build: || {
147 let source = MultipleCopyFileAllocation::new(
152 SimpleGraph::new(3, vec![(0, 1), (1, 2)]),
153 vec![1, 1, 1],
154 vec![5, 5, 5],
155 );
156 crate::example_db::specs::rule_example_via_ilp::<_, bool>(source)
157 },
158 }]
159}
160
161#[cfg(test)]
162#[path = "../unit_tests/rules/multiplecopyfileallocation_ilp.rs"]
163mod tests;