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)]
66pub struct OptimumCommunicationSpanningTree {
67 num_vertices: usize,
68 edge_weights: Vec<Vec<i64>>,
69 requirements: Vec<Vec<i64>>,
70}
71
72#[derive(Debug, Deserialize, crate::CreateSpec)]
73struct OptimumCommunicationSpanningTreeCreateSpec {
74 num_vertices: usize,
76 edge_weights: Option<Vec<Vec<i64>>>,
78 requirements: Vec<Vec<i64>>,
80}
81impl TryFrom<OptimumCommunicationSpanningTreeCreateSpec> for OptimumCommunicationSpanningTree {
82 type Error = crate::registry::ConstructionError;
83 fn try_from(spec: OptimumCommunicationSpanningTreeCreateSpec) -> Result<Self, Self::Error> {
84 let n = spec.num_vertices;
85 if n < 2 {
86 return Err("must have at least two vertices".to_string().into());
87 }
88 let edge_weights = spec.edge_weights.unwrap_or_else(|| {
89 (0..n)
90 .map(|i| (0..n).map(|j| i64::from(i != j)).collect())
91 .collect()
92 });
93 for (name, matrix) in [
94 ("edge_weights", &edge_weights),
95 ("requirements", &spec.requirements),
96 ] {
97 if matrix.len() != n || matrix.iter().any(|row| row.len() != n) {
98 return Err(format!("{name} must be a {n} x {n} matrix").into());
99 }
100 for (i, row) in matrix.iter().enumerate() {
101 if row[i] != 0 {
102 return Err(format!("{name} diagonal must be zero").into());
103 }
104 for (j, &value) in row.iter().enumerate().skip(i + 1) {
105 if value != matrix[j][i] || value < 0 {
106 return Err(format!("{name} must be symmetric and nonnegative").into());
107 }
108 }
109 }
110 }
111 Ok(Self::new(edge_weights, spec.requirements))
112 }
113}
114
115impl OptimumCommunicationSpanningTree {
116 pub fn new(edge_weights: Vec<Vec<i64>>, requirements: Vec<Vec<i64>>) -> Self {
128 let n = edge_weights.len();
129 assert!(n >= 2, "must have at least 2 vertices");
130 assert_eq!(
131 requirements.len(),
132 n,
133 "requirements matrix must have same size as edge_weights"
134 );
135
136 for (i, row) in edge_weights.iter().enumerate() {
137 assert_eq!(
138 row.len(),
139 n,
140 "edge_weights must be square: row {i} has length {} but expected {n}",
141 row.len()
142 );
143 assert_eq!(
144 row[i], 0,
145 "diagonal of edge_weights must be zero: edge_weights[{i}][{i}] = {}",
146 row[i]
147 );
148 }
149
150 for (i, row) in requirements.iter().enumerate() {
151 assert_eq!(
152 row.len(),
153 n,
154 "requirements must be square: row {i} has length {} but expected {n}",
155 row.len()
156 );
157 assert_eq!(
158 row[i], 0,
159 "diagonal of requirements must be zero: requirements[{i}][{i}] = {}",
160 row[i]
161 );
162 }
163
164 for i in 0..n {
166 for j in (i + 1)..n {
167 assert_eq!(
168 edge_weights[i][j], edge_weights[j][i],
169 "edge_weights must be symmetric: w[{i}][{j}]={} != w[{j}][{i}]={}",
170 edge_weights[i][j], edge_weights[j][i]
171 );
172 assert!(
173 edge_weights[i][j] >= 0,
174 "edge_weights must be non-negative: w[{i}][{j}]={}",
175 edge_weights[i][j]
176 );
177 assert_eq!(
178 requirements[i][j], requirements[j][i],
179 "requirements must be symmetric: r[{i}][{j}]={} != r[{j}][{i}]={}",
180 requirements[i][j], requirements[j][i]
181 );
182 assert!(
183 requirements[i][j] >= 0,
184 "requirements must be non-negative: r[{i}][{j}]={}",
185 requirements[i][j]
186 );
187 }
188 }
189
190 Self {
191 num_vertices: n,
192 edge_weights,
193 requirements,
194 }
195 }
196
197 pub fn num_vertices(&self) -> usize {
199 self.num_vertices
200 }
201
202 pub fn num_edges(&self) -> usize {
204 self.num_vertices * (self.num_vertices - 1) / 2
205 }
206
207 pub fn edge_weights(&self) -> &Vec<Vec<i64>> {
209 &self.edge_weights
210 }
211
212 pub fn requirements(&self) -> &Vec<Vec<i64>> {
214 &self.requirements
215 }
216
217 pub fn edges(&self) -> Vec<(usize, usize)> {
219 let n = self.num_vertices;
220 let mut edges = Vec::with_capacity(self.num_edges());
221 for i in 0..n {
222 for j in (i + 1)..n {
223 edges.push((i, j));
224 }
225 }
226 edges
227 }
228
229 pub fn edge_index(i: usize, j: usize, n: usize) -> usize {
231 debug_assert!(i < j && j < n);
232 i * n - i * (i + 1) / 2 + (j - i - 1)
233 }
234}
235
236fn is_valid_spanning_tree(n: usize, edges: &[(usize, usize)], config: &[bool]) -> bool {
238 if config.len() != edges.len() {
239 return false;
240 }
241
242 let selected_count = config.iter().filter(|&&selected| selected).count();
244 if selected_count != n - 1 {
245 return false;
246 }
247
248 let mut adj: Vec<Vec<usize>> = vec![vec![]; n];
250 for (idx, &sel) in config.iter().enumerate() {
251 if sel {
252 let (u, v) = edges[idx];
253 adj[u].push(v);
254 adj[v].push(u);
255 }
256 }
257
258 let mut visited = vec![false; n];
259 let mut queue = VecDeque::new();
260 visited[0] = true;
261 queue.push_back(0);
262 while let Some(v) = queue.pop_front() {
263 for &u in &adj[v] {
264 if !visited[u] {
265 visited[u] = true;
266 queue.push_back(u);
267 }
268 }
269 }
270
271 visited.iter().all(|&v| v)
272}
273
274fn communication_cost(
279 n: usize,
280 edges: &[(usize, usize)],
281 config: &[bool],
282 edge_weights: &[Vec<i64>],
283 requirements: &[Vec<i64>],
284) -> Result<i64, crate::traits::EvaluationError> {
285 let mut adj: Vec<Vec<(usize, i64)>> = vec![vec![]; n];
287 for (idx, &sel) in config.iter().enumerate() {
288 if sel {
289 let (u, v) = edges[idx];
290 let w = edge_weights[u][v];
291 adj[u].push((v, w));
292 adj[v].push((u, w));
293 }
294 }
295
296 let mut total_cost: i64 = 0;
297
298 for src in 0..n {
300 let mut dist = vec![-1i64; n];
301 dist[src] = 0;
302 let mut queue = VecDeque::new();
303 queue.push_back(src);
304 while let Some(u) = queue.pop_front() {
305 for &(v, w) in &adj[u] {
306 if dist[v] < 0 {
307 dist[v] = dist[u].checked_add(w).ok_or_else(|| {
308 crate::traits::EvaluationError::IntegerOverflow(
309 "summing communication-tree path weights".to_string(),
310 )
311 })?;
312 queue.push_back(v);
313 }
314 }
315 }
316
317 for (dst, &d) in dist.iter().enumerate().skip(src + 1) {
319 let term = requirements[src][dst].checked_mul(d).ok_or_else(|| {
320 crate::traits::EvaluationError::IntegerOverflow(
321 "multiplying communication requirement by path weight".to_string(),
322 )
323 })?;
324 total_cost = total_cost.checked_add(term).ok_or_else(|| {
325 crate::traits::EvaluationError::IntegerOverflow(
326 "summing communication-tree costs".to_string(),
327 )
328 })?;
329 }
330 }
331
332 Ok(total_cost)
333}
334
335impl Problem for OptimumCommunicationSpanningTree {
336 const NAME: &'static str = "OptimumCommunicationSpanningTree";
337 type Solution = Vec<bool>;
338 type Value = Min<i64>;
339
340 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
341
342 fn variant() -> Vec<(&'static str, &'static str)> {
343 crate::variant_params![]
344 }
345
346 fn evaluate(
347 &self,
348 config: &Self::Solution,
349 ) -> Result<Min<i64>, crate::traits::EvaluationError> {
350 if config.len() != self.edges().len() {
351 return Err(crate::traits::EvaluationError::InvalidConfiguration(
352 "edge-selection length does not match the complete graph".into(),
353 ));
354 }
355 Ok({
356 let edges = self.edges();
357 if !is_valid_spanning_tree(self.num_vertices, &edges, config) {
358 return Ok(Min(None));
359 }
360 Min(Some(communication_cost(
361 self.num_vertices,
362 &edges,
363 config,
364 &self.edge_weights,
365 &self.requirements,
366 )?))
367 })
368 }
369}
370
371impl crate::solvers::BruteForceProblem for OptimumCommunicationSpanningTree {
372 fn dimensions(&self) -> Vec<usize> {
373 vec![2; self.num_edges()]
374 }
375}
376
377crate::declare_variants! {
378 default OptimumCommunicationSpanningTree => "2^num_edges" create OptimumCommunicationSpanningTreeCreateSpec,
379}
380
381crate::register_brute_force! {
382 OptimumCommunicationSpanningTree decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
383}
384
385#[cfg(feature = "example-db")]
386pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
387 let edge_weights = vec![
395 vec![0, 1, 3, 2],
396 vec![1, 0, 2, 4],
397 vec![3, 2, 0, 1],
398 vec![2, 4, 1, 0],
399 ];
400 let requirements = vec![
401 vec![0, 2, 1, 3],
402 vec![2, 0, 1, 1],
403 vec![1, 1, 0, 2],
404 vec![3, 1, 2, 0],
405 ];
406 vec![crate::example_db::specs::ModelExampleSpec {
409 id: "optimum_communication_spanning_tree",
410 instance: Box::new(OptimumCommunicationSpanningTree::new(
411 edge_weights,
412 requirements,
413 )),
414 optimal_config: serde_json::json!(vec![true, false, true, false, false, true]),
415 optimal_value: serde_json::json!(20),
416 }]
417}
418
419#[cfg(test)]
420#[path = "../../unit_tests/models/misc/optimum_communication_spanning_tree.rs"]
421mod tests;