problemreductions/rules/
minmaxmulticenter_ilp.rs1use crate::models::algebraic::{LinearConstraint, ObjectiveSense, ILP};
28use crate::models::graph::MinMaxMulticenter;
29use crate::reduction;
30use crate::rules::traits::{ReduceTo, ReductionResult};
31use crate::topology::{Graph, SimpleGraph};
32
33#[derive(Debug, Clone)]
35pub struct ReductionMMCToILP {
36 target: ILP<i32>,
37 num_vertices: usize,
38}
39
40impl ReductionResult for ReductionMMCToILP {
41 type Source = MinMaxMulticenter<SimpleGraph, i32>;
42 type Target = ILP<i32>;
43
44 fn target_problem(&self) -> &ILP<i32> {
45 &self.target
46 }
47
48 fn extract_solution(&self, target_solution: &[usize]) -> Vec<usize> {
49 target_solution[..self.num_vertices].to_vec()
50 }
51}
52
53fn weighted_distances_mmc(
57 graph: &SimpleGraph,
58 edge_lengths: &[i32],
59 source: usize,
60 n: usize,
61) -> Vec<Option<i64>> {
62 let mut adj: Vec<Vec<(usize, i64)>> = vec![Vec::new(); n];
63 for (idx, &(u, v)) in graph.edges().iter().enumerate() {
64 let len = i64::from(edge_lengths[idx]);
65 adj[u].push((v, len));
66 adj[v].push((u, len));
67 }
68
69 let mut dist = vec![None; n];
70 let mut visited = vec![false; n];
71 dist[source] = Some(0);
72
73 for _ in 0..n {
74 let mut next = None;
75 for vertex in 0..n {
76 if visited[vertex] {
77 continue;
78 }
79 let Some(dv) = dist[vertex] else {
80 continue;
81 };
82 match next {
83 None => next = Some(vertex),
84 Some(prev) => {
85 if dv < dist[prev].expect("selected vertex must have a distance") {
86 next = Some(vertex);
87 }
88 }
89 }
90 }
91
92 let Some(u) = next else {
93 break;
94 };
95 visited[u] = true;
96 let du = dist[u].expect("selected vertex must have a distance");
97
98 for &(v, len) in &adj[u] {
99 if visited[v] {
100 continue;
101 }
102 let candidate = du + len;
103 let should_update = match dist[v] {
104 None => true,
105 Some(current) => candidate < current,
106 };
107 if should_update {
108 dist[v] = Some(candidate);
109 }
110 }
111 }
112
113 dist
114}
115
116#[reduction(
117 overhead = {
118 num_vars = "num_vertices + num_vertices^2 + 1",
119 num_constraints = "2 * num_vertices^2 + 3 * num_vertices + 2",
120 }
121)]
122impl ReduceTo<ILP<i32>> for MinMaxMulticenter<SimpleGraph, i32> {
123 type Result = ReductionMMCToILP;
124
125 fn reduce_to(&self) -> Self::Result {
126 let n = self.num_vertices();
127 let k = self.k();
128 let vertex_weights = self.vertex_weights();
129 let edge_lengths = self.edge_lengths();
130
131 let all_dist: Vec<Vec<Option<i64>>> = (0..n)
133 .map(|s| weighted_distances_mmc(self.graph(), edge_lengths, s, n))
134 .collect();
135
136 let x_var = |j: usize| j;
138 let y_var = |i: usize, j: usize| n + i * n + j;
139 let z_var = n + n * n;
140
141 let num_vars = n + n * n + 1;
142 let mut constraints = Vec::with_capacity(2 * n * n + 3 * n + 2);
143
144 let center_terms: Vec<(usize, f64)> = (0..n).map(|j| (x_var(j), 1.0)).collect();
146 constraints.push(LinearConstraint::eq(center_terms, k as f64));
147
148 for i in 0..n {
150 let terms: Vec<(usize, f64)> = (0..n).map(|j| (y_var(i, j), 1.0)).collect();
151 constraints.push(LinearConstraint::eq(terms, 1.0));
152 }
153
154 for (i, distances) in all_dist.iter().enumerate() {
157 for (j, distance) in distances.iter().enumerate() {
158 if distance.is_some() {
159 constraints.push(LinearConstraint::le(
160 vec![(y_var(i, j), 1.0), (x_var(j), -1.0)],
161 0.0,
162 ));
163 } else {
164 constraints.push(LinearConstraint::eq(vec![(y_var(i, j), 1.0)], 0.0));
165 }
166 }
167 }
168
169 for j in 0..n {
171 constraints.push(LinearConstraint::le(vec![(x_var(j), 1.0)], 1.0));
172 }
173 for i in 0..n {
174 for j in 0..n {
175 constraints.push(LinearConstraint::le(vec![(y_var(i, j), 1.0)], 1.0));
176 }
177 }
178
179 let z_upper: f64 = all_dist
182 .iter()
183 .enumerate()
184 .flat_map(|(i, row)| {
185 row.iter()
186 .filter_map(move |d| d.map(|d| (vertex_weights[i] as f64) * (d as f64)))
187 })
188 .fold(0.0_f64, f64::max);
189 constraints.push(LinearConstraint::le(vec![(z_var, 1.0)], z_upper));
190
191 for (i, &w) in vertex_weights.iter().enumerate() {
193 let w_i = w as f64;
194 let mut terms: Vec<(usize, f64)> = all_dist[i]
195 .iter()
196 .enumerate()
197 .filter_map(|(j, distance)| {
198 distance.map(|distance| (y_var(i, j), w_i * distance as f64))
199 })
200 .collect();
201 terms.push((z_var, -1.0));
202 constraints.push(LinearConstraint::le(terms, 0.0));
203 }
204
205 let objective = vec![(z_var, 1.0)];
207
208 let target = ILP::new(num_vars, constraints, objective, ObjectiveSense::Minimize);
209 ReductionMMCToILP {
210 target,
211 num_vertices: n,
212 }
213 }
214}
215
216#[cfg(feature = "example-db")]
217pub(crate) fn canonical_rule_example_specs() -> Vec<crate::example_db::specs::RuleExampleSpec> {
218 vec![crate::example_db::specs::RuleExampleSpec {
219 id: "minmaxmulticenter_to_ilp",
220 build: || {
221 let source = MinMaxMulticenter::new(
224 SimpleGraph::new(3, vec![(0, 1), (1, 2)]),
225 vec![1i32; 3],
226 vec![1i32; 2],
227 1,
228 );
229 crate::example_db::specs::rule_example_via_ilp::<_, i32>(source)
230 },
231 }]
232}
233
234#[cfg(test)]
235#[path = "../unit_tests/rules/minmaxmulticenter_ilp.rs"]
236mod tests;