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)]
52pub struct MinimumCapacitatedSpanningTree<G, W: WeightElement> {
53 graph: G,
55 weights: Vec<W>,
57 root: usize,
59 requirements: Vec<W>,
61 capacity: W::Sum,
63}
64
65#[derive(Debug, Deserialize, crate::CreateSpec)]
66struct MinimumCapacitatedSpanningTreeCreateSpec {
67 graph: SimpleGraph,
69 weights: Option<Vec<i64>>,
71 root: usize,
73 requirements: Vec<i64>,
75 capacity: i64,
77}
78impl TryFrom<MinimumCapacitatedSpanningTreeCreateSpec>
79 for MinimumCapacitatedSpanningTree<SimpleGraph, i64>
80{
81 type Error = crate::registry::ConstructionError;
82 fn try_from(spec: MinimumCapacitatedSpanningTreeCreateSpec) -> Result<Self, Self::Error> {
83 let edges = spec.graph.num_edges();
84 let weights = spec.weights.unwrap_or_else(|| vec![1; edges]);
85 if weights.len() != edges {
86 return Err(format!("weights has {} entries, expected {edges}", weights.len()).into());
87 }
88 let vertices = spec.graph.num_vertices();
89 if vertices < 2 {
90 return Err("graph must have at least two vertices".to_string().into());
91 }
92 if spec.requirements.len() != vertices {
93 return Err(format!(
94 "requirements has {} entries, expected {vertices}",
95 spec.requirements.len()
96 )
97 .into());
98 }
99 if spec.root >= vertices {
100 return Err("root is outside the graph".to_string().into());
101 }
102 Ok(Self::new(
103 spec.graph,
104 weights,
105 spec.root,
106 spec.requirements,
107 spec.capacity,
108 ))
109 }
110}
111
112impl<G: Graph, W: WeightElement> MinimumCapacitatedSpanningTree<G, W> {
113 pub fn new(
121 graph: G,
122 weights: Vec<W>,
123 root: usize,
124 requirements: Vec<W>,
125 capacity: W::Sum,
126 ) -> Self {
127 assert_eq!(
128 weights.len(),
129 graph.num_edges(),
130 "weights length must match num_edges"
131 );
132 assert_eq!(
133 requirements.len(),
134 graph.num_vertices(),
135 "requirements length must match num_vertices"
136 );
137 assert!(
138 root < graph.num_vertices(),
139 "root {root} out of range (num_vertices = {})",
140 graph.num_vertices()
141 );
142 assert!(
143 graph.num_vertices() >= 2,
144 "graph must have at least 2 vertices"
145 );
146 Self {
147 graph,
148 weights,
149 root,
150 requirements,
151 capacity,
152 }
153 }
154
155 pub fn graph(&self) -> &G {
157 &self.graph
158 }
159
160 pub fn weights(&self) -> &[W] {
162 &self.weights
163 }
164
165 pub fn set_weights(&mut self, weights: Vec<W>) {
167 assert_eq!(weights.len(), self.graph.num_edges());
168 self.weights = weights;
169 }
170
171 pub fn is_weighted(&self) -> bool {
173 !W::IS_UNIT
174 }
175
176 pub fn root(&self) -> usize {
178 self.root
179 }
180
181 pub fn requirements(&self) -> &[W] {
183 &self.requirements
184 }
185
186 pub fn capacity(&self) -> &W::Sum {
188 &self.capacity
189 }
190
191 pub fn num_vertices(&self) -> usize {
193 self.graph.num_vertices()
194 }
195
196 pub fn num_edges(&self) -> usize {
198 self.graph.num_edges()
199 }
200
201 pub fn is_valid_solution(
203 &self,
204 config: &[bool],
205 ) -> Result<bool, crate::traits::EvaluationError> {
206 is_valid_capacitated_spanning_tree(
207 &self.graph,
208 &self.requirements,
209 self.root,
210 &self.capacity,
211 config,
212 )
213 }
214}
215
216fn is_spanning_tree<G: Graph>(graph: &G, config: &[bool]) -> bool {
220 let n = graph.num_vertices();
221 let edges = graph.edges();
222 if config.len() != edges.len() {
223 return false;
224 }
225
226 let selected_count = config.iter().filter(|&&selected| selected).count();
227 if selected_count != n - 1 {
228 return false;
229 }
230
231 let mut adj: Vec<Vec<usize>> = vec![vec![]; n];
233 for (idx, &sel) in config.iter().enumerate() {
234 if sel {
235 let (u, v) = edges[idx];
236 adj[u].push(v);
237 adj[v].push(u);
238 }
239 }
240
241 let mut visited = vec![false; n];
242 let mut queue = std::collections::VecDeque::new();
243 visited[0] = true;
244 queue.push_back(0);
245 while let Some(v) = queue.pop_front() {
246 for &u in &adj[v] {
247 if !visited[u] {
248 visited[u] = true;
249 queue.push_back(u);
250 }
251 }
252 }
253
254 visited.iter().all(|&v| v)
255}
256
257fn check_capacity<G: Graph, W: WeightElement>(
260 graph: &G,
261 requirements: &[W],
262 root: usize,
263 capacity: &W::Sum,
264 config: &[bool],
265) -> Result<bool, crate::traits::EvaluationError> {
266 let n = graph.num_vertices();
267 let edges = graph.edges();
268
269 let mut adj: Vec<Vec<(usize, usize)>> = vec![vec![]; n]; for (idx, &sel) in config.iter().enumerate() {
272 if sel {
273 let (u, v) = edges[idx];
274 adj[u].push((v, idx));
275 adj[v].push((u, idx));
276 }
277 }
278
279 let mut parent = vec![usize::MAX; n];
281 let mut order = Vec::with_capacity(n);
282 let mut visited = vec![false; n];
283 let mut queue = std::collections::VecDeque::new();
284 visited[root] = true;
285 parent[root] = root;
286 queue.push_back(root);
287 while let Some(v) = queue.pop_front() {
288 order.push(v);
289 for &(u, _) in &adj[v] {
290 if !visited[u] {
291 visited[u] = true;
292 parent[u] = v;
293 queue.push_back(u);
294 }
295 }
296 }
297
298 let mut subtree_sum: Vec<W::Sum> = requirements.iter().map(|r| r.to_sum()).collect();
300 for &v in order.iter().rev() {
301 if v != root {
302 let p = parent[v];
303 let sv = subtree_sum[v].clone();
304 subtree_sum[p] = W::checked_add_to_sum(
305 subtree_sum[p].clone(),
306 sv,
307 "summing capacitated spanning tree requirements",
308 )?;
309 }
310 }
311
312 for (v, sum) in subtree_sum.iter().enumerate() {
314 if v != root && *sum > *capacity {
315 return Ok(false);
316 }
317 }
318
319 Ok(true)
320}
321
322fn is_valid_capacitated_spanning_tree<G: Graph, W: WeightElement>(
324 graph: &G,
325 requirements: &[W],
326 root: usize,
327 capacity: &W::Sum,
328 config: &[bool],
329) -> Result<bool, crate::traits::EvaluationError> {
330 if !is_spanning_tree(graph, config) {
331 return Ok(false);
332 }
333 check_capacity(graph, requirements, root, capacity, config)
334}
335
336impl<G, W> Problem for MinimumCapacitatedSpanningTree<G, W>
337where
338 G: Graph + crate::variant::VariantParam,
339 W: WeightElement + crate::variant::VariantParam,
340{
341 const NAME: &'static str = "MinimumCapacitatedSpanningTree";
342 type Solution = Vec<bool>;
343 type Value = Min<W::Sum>;
344
345 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
346
347 fn variant() -> Vec<(&'static str, &'static str)> {
348 crate::variant_params![G, W]
349 }
350
351 fn evaluate(
352 &self,
353 config: &Self::Solution,
354 ) -> Result<Min<W::Sum>, crate::traits::EvaluationError> {
355 if config.len() != self.graph.num_edges() {
356 return Err(crate::traits::EvaluationError::InvalidConfiguration(
357 "edge-selection length does not match the graph".into(),
358 ));
359 }
360 Ok({
361 if !is_valid_capacitated_spanning_tree(
362 &self.graph,
363 &self.requirements,
364 self.root,
365 &self.capacity,
366 config,
367 )? {
368 return Ok(Min(None));
369 }
370 let mut total = W::Sum::zero();
371 for (idx, &selected) in config.iter().enumerate() {
372 if selected {
373 if let Some(w) = self.weights.get(idx) {
374 total = W::checked_add_to_sum(
375 total,
376 w.to_sum(),
377 "summing capacitated spanning tree edge weights",
378 )?;
379 }
380 }
381 }
382 Min(Some(total))
383 })
384 }
385}
386
387impl<G, W> crate::solvers::BruteForceProblem for MinimumCapacitatedSpanningTree<G, W>
388where
389 G: Graph + crate::variant::VariantParam,
390 W: WeightElement + crate::variant::VariantParam,
391{
392 fn dimensions(&self) -> Vec<usize> {
393 vec![2; self.graph.num_edges()]
394 }
395}
396
397crate::declare_variants! {
398 default MinimumCapacitatedSpanningTree<SimpleGraph, i64> => "2^num_edges" create MinimumCapacitatedSpanningTreeCreateSpec,
399}
400
401crate::register_brute_force! {
402 MinimumCapacitatedSpanningTree<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
403}
404
405#[cfg(feature = "example-db")]
406pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
407 vec![crate::example_db::specs::ModelExampleSpec {
408 id: "minimum_capacitated_spanning_tree_simplegraph",
409 instance: Box::new(MinimumCapacitatedSpanningTree::new(
410 SimpleGraph::new(
411 5,
412 vec![
413 (0, 1),
414 (0, 2),
415 (0, 3),
416 (1, 2),
417 (1, 4),
418 (2, 3),
419 (2, 4),
420 (3, 4),
421 ],
422 ),
423 vec![2, 1, 4, 3, 1, 2, 3, 1], 0, vec![0, 1, 1, 1, 1], 3, )),
428 optimal_config: serde_json::json!(vec![true, true, false, false, true, false, false, true]),
433 optimal_value: serde_json::json!(5),
434 }]
435}
436
437#[cfg(test)]
438#[path = "../../unit_tests/models/graph/minimum_capacitated_spanning_tree.rs"]
439mod tests;