1use num_traits::Zero;
9use serde::{Deserialize, Serialize};
10
11use crate::registry::{CreateSpec, ProblemSchemaEntry, VariantDimension};
12use crate::topology::{Graph, SimpleGraph};
13use crate::traits::Problem;
14use crate::types::{Min, WeightElement};
15
16inventory::submit! {
17 ProblemSchemaEntry {
18 name: "MinimumCapacitatedSpanningTree",
19 display_name: "Minimum Capacitated Spanning Tree",
20 aliases: &["MCST"],
21 dimensions: &[
22 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
23 VariantDimension::new("weight", "i64", &["i64"]),
24 ],
25 category: crate::registry::ProblemCategory::Graph,
26 module_path: module_path!(),
27 description: "Find minimum weight spanning tree with subtree capacity constraints",
28 fields: MinimumCapacitatedSpanningTreeCreateSpec::FIELDS,
29 }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
52#[serde(try_from = "MinimumCapacitatedSpanningTreeData<G, W>")]
53#[serde(bound(
54 deserialize = "G: Graph + Deserialize<'de>, W: WeightElement + Deserialize<'de>, W::Sum: Deserialize<'de>"
55))]
56pub struct MinimumCapacitatedSpanningTree<G, W: WeightElement> {
57 graph: G,
59 weights: Vec<W>,
61 root: usize,
63 requirements: Vec<W>,
65 capacity: W::Sum,
67}
68
69#[derive(Deserialize)]
70#[serde(bound(
71 deserialize = "G: Graph + Deserialize<'de>, W: WeightElement + Deserialize<'de>, W::Sum: Deserialize<'de>"
72))]
73struct MinimumCapacitatedSpanningTreeData<G, W: WeightElement> {
74 graph: G,
75 weights: Vec<W>,
76 root: usize,
77 requirements: Vec<W>,
78 capacity: W::Sum,
79}
80
81impl<G, W> TryFrom<MinimumCapacitatedSpanningTreeData<G, W>>
82 for MinimumCapacitatedSpanningTree<G, W>
83where
84 G: Graph,
85 W: WeightElement,
86{
87 type Error = crate::registry::ConstructionError;
88
89 fn try_from(data: MinimumCapacitatedSpanningTreeData<G, W>) -> Result<Self, Self::Error> {
90 Self::try_new(
91 data.graph,
92 data.weights,
93 data.root,
94 data.requirements,
95 data.capacity,
96 )
97 }
98}
99
100#[derive(Debug, Deserialize, crate::CreateSpec)]
101struct MinimumCapacitatedSpanningTreeCreateSpec {
102 graph: SimpleGraph,
104 weights: Option<Vec<i64>>,
106 root: usize,
108 requirements: Vec<i64>,
110 capacity: i64,
112}
113impl TryFrom<MinimumCapacitatedSpanningTreeCreateSpec>
114 for MinimumCapacitatedSpanningTree<SimpleGraph, i64>
115{
116 type Error = crate::registry::ConstructionError;
117 fn try_from(spec: MinimumCapacitatedSpanningTreeCreateSpec) -> Result<Self, Self::Error> {
118 let edges = spec.graph.num_edges();
119 let weights = spec.weights.unwrap_or_else(|| vec![1; edges]);
120 Self::try_new(
121 spec.graph,
122 weights,
123 spec.root,
124 spec.requirements,
125 spec.capacity,
126 )
127 }
128}
129
130impl<G: Graph, W: WeightElement> MinimumCapacitatedSpanningTree<G, W> {
131 pub fn new(
139 graph: G,
140 weights: Vec<W>,
141 root: usize,
142 requirements: Vec<W>,
143 capacity: W::Sum,
144 ) -> Self {
145 Self::try_new(graph, weights, root, requirements, capacity)
146 .unwrap_or_else(|error| panic!("{error}"))
147 }
148
149 fn try_new(
150 graph: G,
151 weights: Vec<W>,
152 root: usize,
153 requirements: Vec<W>,
154 capacity: W::Sum,
155 ) -> Result<Self, crate::registry::ConstructionError> {
156 Self::check_weights(&graph, &weights)?;
157 if requirements.len() != graph.num_vertices() {
158 return Err(crate::registry::ConstructionError::length_mismatch(
159 "requirements",
160 requirements.len(),
161 graph.num_vertices(),
162 ));
163 }
164 if root >= graph.num_vertices() {
165 return Err(format!(
166 "root {root} out of range (num_vertices = {})",
167 graph.num_vertices()
168 )
169 .into());
170 }
171 if graph.num_vertices() < 2 {
172 return Err("graph must have at least 2 vertices".into());
173 }
174 Ok(Self {
175 graph,
176 weights,
177 root,
178 requirements,
179 capacity,
180 })
181 }
182
183 pub fn graph(&self) -> &G {
185 &self.graph
186 }
187
188 pub fn weights(&self) -> &[W] {
190 &self.weights
191 }
192
193 pub fn set_weights(&mut self, weights: Vec<W>) {
195 Self::check_weights(&self.graph, &weights).unwrap_or_else(|error| panic!("{error}"));
196 self.weights = weights;
197 }
198
199 fn check_weights(graph: &G, weights: &[W]) -> Result<(), crate::registry::ConstructionError> {
200 if weights.len() != graph.num_edges() {
201 return Err(crate::registry::ConstructionError::length_mismatch(
202 "weights",
203 weights.len(),
204 graph.num_edges(),
205 ));
206 }
207 Ok(())
208 }
209
210 pub fn is_weighted(&self) -> bool {
212 !W::IS_UNIT
213 }
214
215 pub fn root(&self) -> usize {
217 self.root
218 }
219
220 pub fn requirements(&self) -> &[W] {
222 &self.requirements
223 }
224
225 pub fn capacity(&self) -> &W::Sum {
227 &self.capacity
228 }
229
230 pub fn max_requirement_bits(&self) -> u64 {
232 crate::types::max_numeric_magnitude_bits(
233 self.requirements
234 .iter()
235 .map(|value| value.to_sum())
236 .chain(std::iter::once(self.capacity.clone())),
237 )
238 }
239
240 pub fn num_vertices(&self) -> usize {
242 self.graph.num_vertices()
243 }
244
245 pub fn num_edges(&self) -> usize {
247 self.graph.num_edges()
248 }
249
250 pub fn is_valid_solution(
252 &self,
253 config: &[bool],
254 ) -> Result<bool, crate::traits::EvaluationError> {
255 is_valid_capacitated_spanning_tree(
256 &self.graph,
257 &self.requirements,
258 self.root,
259 &self.capacity,
260 config,
261 )
262 }
263}
264
265fn is_spanning_tree<G: Graph>(graph: &G, config: &[bool]) -> bool {
269 let n = graph.num_vertices();
270 let edges = graph.edges();
271 if config.len() != edges.len() {
272 return false;
273 }
274
275 let selected_count = config.iter().filter(|&&selected| selected).count();
276 if selected_count != n - 1 {
277 return false;
278 }
279
280 let mut adj: Vec<Vec<usize>> = vec![vec![]; n];
282 for (idx, &sel) in config.iter().enumerate() {
283 if sel {
284 let (u, v) = edges[idx];
285 adj[u].push(v);
286 adj[v].push(u);
287 }
288 }
289
290 let mut visited = vec![false; n];
291 let mut queue = std::collections::VecDeque::new();
292 visited[0] = true;
293 queue.push_back(0);
294 while let Some(v) = queue.pop_front() {
295 for &u in &adj[v] {
296 if !visited[u] {
297 visited[u] = true;
298 queue.push_back(u);
299 }
300 }
301 }
302
303 visited.iter().all(|&v| v)
304}
305
306fn check_capacity<G: Graph, W: WeightElement>(
309 graph: &G,
310 requirements: &[W],
311 root: usize,
312 capacity: &W::Sum,
313 config: &[bool],
314) -> Result<bool, crate::traits::EvaluationError> {
315 let n = graph.num_vertices();
316 let edges = graph.edges();
317
318 let mut adj: Vec<Vec<(usize, usize)>> = vec![vec![]; n]; for (idx, &sel) in config.iter().enumerate() {
321 if sel {
322 let (u, v) = edges[idx];
323 adj[u].push((v, idx));
324 adj[v].push((u, idx));
325 }
326 }
327
328 let mut parent = vec![usize::MAX; n];
330 let mut order = Vec::with_capacity(n);
331 let mut visited = vec![false; n];
332 let mut queue = std::collections::VecDeque::new();
333 visited[root] = true;
334 parent[root] = root;
335 queue.push_back(root);
336 while let Some(v) = queue.pop_front() {
337 order.push(v);
338 for &(u, _) in &adj[v] {
339 if !visited[u] {
340 visited[u] = true;
341 parent[u] = v;
342 queue.push_back(u);
343 }
344 }
345 }
346
347 let mut subtree_sum: Vec<W::Sum> = requirements.iter().map(|r| r.to_sum()).collect();
349 for &v in order.iter().rev() {
350 if v != root {
351 let p = parent[v];
352 let sv = subtree_sum[v].clone();
353 subtree_sum[p] = W::checked_add_to_sum(
354 subtree_sum[p].clone(),
355 sv,
356 "summing capacitated spanning tree requirements",
357 )?;
358 }
359 }
360
361 for (v, sum) in subtree_sum.iter().enumerate() {
363 if v != root && *sum > *capacity {
364 return Ok(false);
365 }
366 }
367
368 Ok(true)
369}
370
371fn is_valid_capacitated_spanning_tree<G: Graph, W: WeightElement>(
373 graph: &G,
374 requirements: &[W],
375 root: usize,
376 capacity: &W::Sum,
377 config: &[bool],
378) -> Result<bool, crate::traits::EvaluationError> {
379 if !is_spanning_tree(graph, config) {
380 return Ok(false);
381 }
382 check_capacity(graph, requirements, root, capacity, config)
383}
384
385impl<G, W> Problem for MinimumCapacitatedSpanningTree<G, W>
386where
387 G: Graph + crate::variant::VariantParam,
388 W: WeightElement + crate::variant::VariantParam,
389{
390 const NAME: &'static str = "MinimumCapacitatedSpanningTree";
391 type Solution = Vec<bool>;
392 type Value = Min<W::Sum>;
393
394 crate::problem_parameters![
395 ("max_requirement_bits", max_requirement_bits),
396 ("num_edges", num_edges),
397 ("num_vertices", num_vertices),
398 ];
399
400 fn variant() -> Vec<(&'static str, &'static str)> {
401 crate::variant_params![G, W]
402 }
403
404 fn evaluate(
405 &self,
406 config: &Self::Solution,
407 ) -> Result<Min<W::Sum>, crate::traits::EvaluationError> {
408 if config.len() != self.graph.num_edges() {
409 return Err(crate::traits::EvaluationError::InvalidConfiguration(
410 "edge-selection length does not match the graph".into(),
411 ));
412 }
413 Ok({
414 if !is_valid_capacitated_spanning_tree(
415 &self.graph,
416 &self.requirements,
417 self.root,
418 &self.capacity,
419 config,
420 )? {
421 return Ok(Min(None));
422 }
423 let mut total = W::Sum::zero();
424 for (idx, &selected) in config.iter().enumerate() {
425 if selected {
426 if let Some(w) = self.weights.get(idx) {
427 total = W::checked_add_to_sum(
428 total,
429 w.to_sum(),
430 "summing capacitated spanning tree edge weights",
431 )?;
432 }
433 }
434 }
435 Min(Some(total))
436 })
437 }
438}
439
440impl<G, W> crate::solvers::BruteForceProblem for MinimumCapacitatedSpanningTree<G, W>
441where
442 G: Graph + crate::variant::VariantParam,
443 W: WeightElement + crate::variant::VariantParam,
444{
445 fn dimensions(&self) -> Vec<usize> {
446 vec![2; self.graph.num_edges()]
447 }
448}
449
450crate::declare_variants! {
451 default MinimumCapacitatedSpanningTree<SimpleGraph, i64> => "2^num_edges" create MinimumCapacitatedSpanningTreeCreateSpec,
452}
453
454crate::register_brute_force! {
455 MinimumCapacitatedSpanningTree<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
456}
457
458#[cfg(feature = "example-db")]
459pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
460 vec![crate::example_db::specs::ModelExampleSpec {
461 id: "minimum_capacitated_spanning_tree_simplegraph",
462 instance: Box::new(MinimumCapacitatedSpanningTree::new(
463 SimpleGraph::new(
464 5,
465 vec![
466 (0, 1),
467 (0, 2),
468 (0, 3),
469 (1, 2),
470 (1, 4),
471 (2, 3),
472 (2, 4),
473 (3, 4),
474 ],
475 ),
476 vec![2, 1, 4, 3, 1, 2, 3, 1], 0, vec![0, 1, 1, 1, 1], 3, )),
481 optimal_config: serde_json::json!(vec![true, true, false, false, true, false, false, true]),
486 optimal_value: serde_json::json!(5),
487 }]
488}
489
490#[cfg(test)]
491#[path = "../../unit_tests/models/graph/minimum_capacitated_spanning_tree.rs"]
492mod tests;