problemreductions/models/misc/
optimum_communication_spanning_tree.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry};
9use crate::traits::Problem;
10use crate::types::Min;
11use serde::{Deserialize, Serialize};
12use std::collections::VecDeque;
13
14inventory::submit! {
15 ProblemSchemaEntry {
16 name: "OptimumCommunicationSpanningTree",
17 display_name: "Optimum Communication Spanning Tree",
18 aliases: &["OCST"],
19 dimensions: &[],
20 category: crate::registry::ProblemCategory::Misc,
21 module_path: module_path!(),
22 description: "Find spanning tree minimizing total weighted communication cost",
23 fields: OptimumCommunicationSpanningTreeCreateSpec::FIELDS,
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
66#[serde(try_from = "OptimumCommunicationSpanningTreeData")]
67pub struct OptimumCommunicationSpanningTree {
68 num_vertices: usize,
69 edge_weights: Vec<Vec<i64>>,
70 requirements: Vec<Vec<i64>>,
71}
72
73#[derive(Deserialize)]
74struct OptimumCommunicationSpanningTreeData {
75 edge_weights: Vec<Vec<i64>>,
76 requirements: Vec<Vec<i64>>,
77}
78
79impl TryFrom<OptimumCommunicationSpanningTreeData> for OptimumCommunicationSpanningTree {
80 type Error = crate::registry::ConstructionError;
81 fn try_from(data: OptimumCommunicationSpanningTreeData) -> Result<Self, Self::Error> {
82 Self::try_new(data.edge_weights, data.requirements)
83 }
84}
85
86#[derive(Debug, Deserialize, crate::CreateSpec)]
87struct OptimumCommunicationSpanningTreeCreateSpec {
88 num_vertices: usize,
90 edge_weights: Option<Vec<Vec<i64>>>,
92 requirements: Vec<Vec<i64>>,
94}
95impl TryFrom<OptimumCommunicationSpanningTreeCreateSpec> for OptimumCommunicationSpanningTree {
96 type Error = crate::registry::ConstructionError;
97 fn try_from(spec: OptimumCommunicationSpanningTreeCreateSpec) -> Result<Self, Self::Error> {
98 let n = spec.num_vertices;
99 let edge_weights = spec.edge_weights.unwrap_or_else(|| {
100 (0..n)
101 .map(|i| (0..n).map(|j| i64::from(i != j)).collect())
102 .collect()
103 });
104 for (name, matrix) in [
105 ("edge_weights", &edge_weights),
106 ("requirements", &spec.requirements),
107 ] {
108 if matrix.len() != n {
109 return Err(format!("{name} must be a {n} x {n} matrix").into());
110 }
111 }
112 Self::try_new(edge_weights, spec.requirements)
113 }
114}
115
116impl OptimumCommunicationSpanningTree {
117 pub fn new(edge_weights: Vec<Vec<i64>>, requirements: Vec<Vec<i64>>) -> Self {
129 Self::try_new(edge_weights, requirements).unwrap_or_else(|error| panic!("{error}"))
130 }
131
132 fn try_new(
133 edge_weights: Vec<Vec<i64>>,
134 requirements: Vec<Vec<i64>>,
135 ) -> Result<Self, crate::registry::ConstructionError> {
136 let n = edge_weights.len();
137 if n < 2 {
138 return Err("must have at least 2 vertices".into());
139 }
140 if requirements.len() != n {
141 return Err("requirements matrix must have same size as edge_weights".into());
142 }
143
144 for (i, row) in edge_weights.iter().enumerate() {
145 if row.len() != n {
146 return Err(format!(
147 "edge_weights must be square: row {i} has length {} but expected {n}",
148 row.len()
149 )
150 .into());
151 }
152 if row[i] != 0 {
153 return Err(format!(
154 "diagonal of edge_weights must be zero: edge_weights[{i}][{i}] = {}",
155 row[i]
156 )
157 .into());
158 }
159 }
160
161 for (i, row) in requirements.iter().enumerate() {
162 if row.len() != n {
163 return Err(format!(
164 "requirements must be square: row {i} has length {} but expected {n}",
165 row.len()
166 )
167 .into());
168 }
169 if row[i] != 0 {
170 return Err(format!(
171 "diagonal of requirements must be zero: requirements[{i}][{i}] = {}",
172 row[i]
173 )
174 .into());
175 }
176 }
177
178 for i in 0..n {
180 for j in (i + 1)..n {
181 if edge_weights[i][j] != edge_weights[j][i] {
182 return Err(format!(
183 "edge_weights must be symmetric: w[{i}][{j}]={} != w[{j}][{i}]={}",
184 edge_weights[i][j], edge_weights[j][i]
185 )
186 .into());
187 }
188 if edge_weights[i][j] < 0 {
189 return Err(format!(
190 "edge_weights must be non-negative: w[{i}][{j}]={}",
191 edge_weights[i][j]
192 )
193 .into());
194 }
195 if requirements[i][j] != requirements[j][i] {
196 return Err(format!(
197 "requirements must be symmetric: r[{i}][{j}]={} != r[{j}][{i}]={}",
198 requirements[i][j], requirements[j][i]
199 )
200 .into());
201 }
202 if requirements[i][j] < 0 {
203 return Err(format!(
204 "requirements must be non-negative: r[{i}][{j}]={}",
205 requirements[i][j]
206 )
207 .into());
208 }
209 }
210 }
211
212 Ok(Self {
213 num_vertices: n,
214 edge_weights,
215 requirements,
216 })
217 }
218
219 pub fn num_vertices(&self) -> usize {
221 self.num_vertices
222 }
223
224 pub fn num_edges(&self) -> usize {
226 self.num_vertices * (self.num_vertices - 1) / 2
227 }
228
229 pub fn edge_weights(&self) -> &Vec<Vec<i64>> {
231 &self.edge_weights
232 }
233
234 pub fn requirements(&self) -> &Vec<Vec<i64>> {
236 &self.requirements
237 }
238
239 pub fn edges(&self) -> Vec<(usize, usize)> {
241 let n = self.num_vertices;
242 let mut edges = Vec::with_capacity(self.num_edges());
243 for i in 0..n {
244 for j in (i + 1)..n {
245 edges.push((i, j));
246 }
247 }
248 edges
249 }
250
251 pub fn edge_index(i: usize, j: usize, n: usize) -> usize {
253 debug_assert!(i < j && j < n);
254 i * n - i * (i + 1) / 2 + (j - i - 1)
255 }
256}
257
258fn is_valid_spanning_tree(n: usize, edges: &[(usize, usize)], config: &[bool]) -> bool {
260 if config.len() != edges.len() {
261 return false;
262 }
263
264 let selected_count = config.iter().filter(|&&selected| selected).count();
266 if selected_count != n - 1 {
267 return false;
268 }
269
270 let mut adj: Vec<Vec<usize>> = vec![vec![]; n];
272 for (idx, &sel) in config.iter().enumerate() {
273 if sel {
274 let (u, v) = edges[idx];
275 adj[u].push(v);
276 adj[v].push(u);
277 }
278 }
279
280 let mut visited = vec![false; n];
281 let mut queue = VecDeque::new();
282 visited[0] = true;
283 queue.push_back(0);
284 while let Some(v) = queue.pop_front() {
285 for &u in &adj[v] {
286 if !visited[u] {
287 visited[u] = true;
288 queue.push_back(u);
289 }
290 }
291 }
292
293 visited.iter().all(|&v| v)
294}
295
296fn communication_cost(
301 n: usize,
302 edges: &[(usize, usize)],
303 config: &[bool],
304 edge_weights: &[Vec<i64>],
305 requirements: &[Vec<i64>],
306) -> Result<i64, crate::traits::EvaluationError> {
307 let mut adj: Vec<Vec<(usize, i64)>> = vec![vec![]; n];
309 for (idx, &sel) in config.iter().enumerate() {
310 if sel {
311 let (u, v) = edges[idx];
312 let w = edge_weights[u][v];
313 adj[u].push((v, w));
314 adj[v].push((u, w));
315 }
316 }
317
318 let mut total_cost: i64 = 0;
319
320 for src in 0..n {
322 let mut dist = vec![-1i64; n];
323 dist[src] = 0;
324 let mut queue = VecDeque::new();
325 queue.push_back(src);
326 while let Some(u) = queue.pop_front() {
327 for &(v, w) in &adj[u] {
328 if dist[v] < 0 {
329 dist[v] = dist[u].checked_add(w).ok_or_else(|| {
330 crate::traits::EvaluationError::IntegerOverflow(
331 "summing communication-tree path weights".to_string(),
332 )
333 })?;
334 queue.push_back(v);
335 }
336 }
337 }
338
339 for (dst, &d) in dist.iter().enumerate().skip(src + 1) {
341 let term = requirements[src][dst].checked_mul(d).ok_or_else(|| {
342 crate::traits::EvaluationError::IntegerOverflow(
343 "multiplying communication requirement by path weight".to_string(),
344 )
345 })?;
346 total_cost = total_cost.checked_add(term).ok_or_else(|| {
347 crate::traits::EvaluationError::IntegerOverflow(
348 "summing communication-tree costs".to_string(),
349 )
350 })?;
351 }
352 }
353
354 Ok(total_cost)
355}
356
357impl Problem for OptimumCommunicationSpanningTree {
358 const NAME: &'static str = "OptimumCommunicationSpanningTree";
359 type Solution = Vec<bool>;
360 type Value = Min<i64>;
361
362 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
363
364 fn variant() -> Vec<(&'static str, &'static str)> {
365 crate::variant_params![]
366 }
367
368 fn evaluate(
369 &self,
370 config: &Self::Solution,
371 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
372 if config.len() != self.edges().len() {
373 return Err(crate::traits::EvaluationError::InvalidConfiguration(
374 "edge-selection length does not match the complete graph".into(),
375 ));
376 }
377 Ok({
378 let edges = self.edges();
379 if !is_valid_spanning_tree(self.num_vertices, &edges, config) {
380 return Ok(Min(None));
381 }
382 Min(Some(communication_cost(
383 self.num_vertices,
384 &edges,
385 config,
386 &self.edge_weights,
387 &self.requirements,
388 )?))
389 })
390 }
391}
392
393impl crate::solvers::BruteForceProblem for OptimumCommunicationSpanningTree {
394 fn dimensions(&self) -> Vec<usize> {
395 vec![2; self.num_edges()]
396 }
397}
398
399crate::declare_variants! {
400 default OptimumCommunicationSpanningTree => "2^num_edges" create OptimumCommunicationSpanningTreeCreateSpec,
401}
402
403crate::register_brute_force! {
404 OptimumCommunicationSpanningTree decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
405}
406
407#[cfg(feature = "example-db")]
408pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
409 let edge_weights = vec![
417 vec![0, 1, 3, 2],
418 vec![1, 0, 2, 4],
419 vec![3, 2, 0, 1],
420 vec![2, 4, 1, 0],
421 ];
422 let requirements = vec![
423 vec![0, 2, 1, 3],
424 vec![2, 0, 1, 1],
425 vec![1, 1, 0, 2],
426 vec![3, 1, 2, 0],
427 ];
428 vec![crate::example_db::specs::ModelExampleSpec {
431 id: "optimum_communication_spanning_tree",
432 instance: Box::new(OptimumCommunicationSpanningTree::new(
433 edge_weights,
434 requirements,
435 )),
436 optimal_config: serde_json::json!(vec![true, false, true, false, false, true]),
437 optimal_value: serde_json::json!(20),
438 }]
439}
440
441#[cfg(test)]
442#[path = "../../unit_tests/models/misc/optimum_communication_spanning_tree.rs"]
443mod tests;