problemreductions/models/graph/
minimum_cut_into_bounded_sets.rs1use crate::registry::{CreateSpec, ProblemSchemaEntry, VariantDimension};
8use crate::topology::{Graph, SimpleGraph};
9use crate::traits::Problem;
10use crate::types::{Min, WeightElement};
11use num_traits::Zero;
12use serde::{Deserialize, Serialize};
13
14inventory::submit! {
15 ProblemSchemaEntry {
16 name: "MinimumCutIntoBoundedSets",
17 display_name: "Minimum Cut Into Bounded Sets",
18 aliases: &[],
19 dimensions: &[
20 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
21 VariantDimension::new("weight", "i64", &["i64"]),
22 ],
23 category: crate::registry::ProblemCategory::Graph,
24 module_path: module_path!(),
25 description: "Find a minimum-weight cut partitioning vertices into two bounded-size sets",
26 fields: MinimumCutIntoBoundedSetsCreateSpec::FIELDS,
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct MinimumCutIntoBoundedSets<G, W: WeightElement> {
61 graph: G,
63 edge_weights: Vec<W>,
65 source: usize,
67 sink: usize,
69 size_bound: usize,
71}
72
73#[derive(Debug, Deserialize, crate::CreateSpec)]
74struct MinimumCutIntoBoundedSetsCreateSpec {
75 graph: SimpleGraph,
77 edge_weights: Option<Vec<i64>>,
79 source: usize,
81 sink: usize,
83 size_bound: usize,
85}
86impl TryFrom<MinimumCutIntoBoundedSetsCreateSpec> for MinimumCutIntoBoundedSets<SimpleGraph, i64> {
87 type Error = crate::registry::ConstructionError;
88 fn try_from(spec: MinimumCutIntoBoundedSetsCreateSpec) -> Result<Self, Self::Error> {
89 let count = spec.graph.num_edges();
90 let edge_weights = spec.edge_weights.unwrap_or_else(|| vec![1; count]);
91 if edge_weights.len() != count {
92 return Err(format!(
93 "edge_weights has {} entries, expected {count}",
94 edge_weights.len()
95 )
96 .into());
97 }
98 let vertices = spec.graph.num_vertices();
99 if spec.source >= vertices || spec.sink >= vertices || spec.source == spec.sink {
100 return Err("source and sink must be distinct valid graph vertices"
101 .to_string()
102 .into());
103 }
104 Ok(Self::new(
105 spec.graph,
106 edge_weights,
107 spec.source,
108 spec.sink,
109 spec.size_bound,
110 ))
111 }
112}
113
114impl<G: Graph, W: WeightElement> MinimumCutIntoBoundedSets<G, W> {
115 pub fn new(
128 graph: G,
129 edge_weights: Vec<W>,
130 source: usize,
131 sink: usize,
132 size_bound: usize,
133 ) -> Self {
134 assert_eq!(
135 edge_weights.len(),
136 graph.num_edges(),
137 "edge_weights length must match num_edges"
138 );
139 assert!(source < graph.num_vertices(), "source vertex out of bounds");
140 assert!(sink < graph.num_vertices(), "sink vertex out of bounds");
141 assert_ne!(source, sink, "source and sink must be different vertices");
142 Self {
143 graph,
144 edge_weights,
145 source,
146 sink,
147 size_bound,
148 }
149 }
150
151 pub fn graph(&self) -> &G {
153 &self.graph
154 }
155
156 pub fn edge_weights(&self) -> &[W] {
158 &self.edge_weights
159 }
160
161 pub fn source(&self) -> usize {
163 self.source
164 }
165
166 pub fn sink(&self) -> usize {
168 self.sink
169 }
170
171 pub fn size_bound(&self) -> usize {
173 self.size_bound
174 }
175
176 pub fn num_vertices(&self) -> usize {
178 self.graph.num_vertices()
179 }
180
181 pub fn num_edges(&self) -> usize {
183 self.graph.num_edges()
184 }
185}
186
187impl<G, W> Problem for MinimumCutIntoBoundedSets<G, W>
188where
189 G: Graph + crate::variant::VariantParam,
190 W: WeightElement + crate::variant::VariantParam,
191{
192 const NAME: &'static str = "MinimumCutIntoBoundedSets";
193 type Solution = Vec<bool>;
194 type Value = Min<W::Sum>;
195
196 crate::problem_parameters![("num_edges", num_edges), ("num_vertices", num_vertices),];
197
198 fn variant() -> Vec<(&'static str, &'static str)> {
199 crate::variant_params![G, W]
200 }
201
202 fn evaluate(
203 &self,
204 config: &Self::Solution,
205 ) -> Result<Min<W::Sum>, crate::traits::EvaluationError> {
206 Ok({
207 let n = self.graph.num_vertices();
208 if config.len() != n {
209 return Err(crate::traits::EvaluationError::InvalidConfiguration(
210 "partition assignment length does not match the graph vertices".into(),
211 ));
212 }
213
214 if config[self.source] || !config[self.sink] {
216 return Ok(Min(None));
217 }
218
219 let count_v1 = config.iter().filter(|&&x| !x).count();
221 let count_v2 = config.iter().filter(|&&x| x).count();
222 if count_v1 > self.size_bound || count_v2 > self.size_bound {
223 return Ok(Min(None));
224 }
225
226 let mut cut_weight = W::Sum::zero();
228 for ((u, v), weight) in self.graph.edges().iter().zip(self.edge_weights.iter()) {
229 if config[*u] != config[*v] {
230 cut_weight = W::checked_add_to_sum(
231 cut_weight,
232 weight.to_sum(),
233 "summing bounded-set cut weights",
234 )?;
235 }
236 }
237
238 Min(Some(cut_weight))
239 })
240 }
241}
242
243impl<G, W> crate::solvers::BruteForceProblem for MinimumCutIntoBoundedSets<G, W>
244where
245 G: Graph + crate::variant::VariantParam,
246 W: WeightElement + crate::variant::VariantParam,
247{
248 fn dimensions(&self) -> Vec<usize> {
249 vec![2; self.graph.num_vertices()]
250 }
251}
252
253#[cfg(feature = "example-db")]
254pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
255 vec![crate::example_db::specs::ModelExampleSpec {
256 id: "minimum_cut_into_bounded_sets",
257 instance: Box::new(MinimumCutIntoBoundedSets::new(
258 SimpleGraph::new(
259 8,
260 vec![
261 (0, 1),
262 (0, 2),
263 (1, 2),
264 (1, 3),
265 (2, 4),
266 (3, 5),
267 (3, 6),
268 (4, 5),
269 (4, 6),
270 (5, 7),
271 (6, 7),
272 (5, 6),
273 ],
274 ),
275 vec![2, 3, 1, 4, 2, 1, 3, 2, 1, 2, 3, 1],
276 0,
277 7,
278 5,
279 )),
280 optimal_config: serde_json::json!(vec![false, false, false, false, true, true, true, true]),
282 optimal_value: serde_json::json!(6),
283 }]
284}
285
286crate::impl_random_generate!(MinimumCutIntoBoundedSets<SimpleGraph, i64>, crate::random::EndpointRandomSpec, |spec| {
287 let (source, sink) = spec.endpoints()?;
288 let graph = spec.graph()?;
289 let edge_weights = vec![1; graph.num_edges()];
290 Ok(MinimumCutIntoBoundedSets::new(graph, edge_weights, source, sink, spec.num_vertices))
291});
292
293crate::declare_variants! {
294 default MinimumCutIntoBoundedSets<SimpleGraph, i64> => "2^num_vertices" create MinimumCutIntoBoundedSetsCreateSpec random,
295}
296
297crate::register_brute_force! {
298 MinimumCutIntoBoundedSets<SimpleGraph, i64> decode |_, indices: Vec<usize>| crate::config::config_to_bits(&indices),
299}
300
301#[cfg(test)]
302#[path = "../../unit_tests/models/graph/minimum_cut_into_bounded_sets.rs"]
303mod tests;