problemreductions/models/graph/
minimum_cut_into_bounded_sets.rs1use crate::registry::{FieldInfo, 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", "i32", &["i32"]),
22 ],
23 module_path: module_path!(),
24 description: "Find a minimum-weight cut partitioning vertices into two bounded-size sets",
25 fields: &[
26 FieldInfo { name: "graph", type_name: "G", description: "The undirected graph G = (V, E)" },
27 FieldInfo { name: "edge_weights", type_name: "Vec<W>", description: "Edge weights w: E -> Z+" },
28 FieldInfo { name: "source", type_name: "usize", description: "Source vertex s (must be in V1)" },
29 FieldInfo { name: "sink", type_name: "usize", description: "Sink vertex t (must be in V2)" },
30 FieldInfo { name: "size_bound", type_name: "usize", description: "Maximum size B for each partition set" },
31 ],
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
65pub struct MinimumCutIntoBoundedSets<G, W: WeightElement> {
66 graph: G,
68 edge_weights: Vec<W>,
70 source: usize,
72 sink: usize,
74 size_bound: usize,
76}
77
78impl<G: Graph, W: WeightElement> MinimumCutIntoBoundedSets<G, W> {
79 pub fn new(
92 graph: G,
93 edge_weights: Vec<W>,
94 source: usize,
95 sink: usize,
96 size_bound: usize,
97 ) -> Self {
98 assert_eq!(
99 edge_weights.len(),
100 graph.num_edges(),
101 "edge_weights length must match num_edges"
102 );
103 assert!(source < graph.num_vertices(), "source vertex out of bounds");
104 assert!(sink < graph.num_vertices(), "sink vertex out of bounds");
105 assert_ne!(source, sink, "source and sink must be different vertices");
106 Self {
107 graph,
108 edge_weights,
109 source,
110 sink,
111 size_bound,
112 }
113 }
114
115 pub fn graph(&self) -> &G {
117 &self.graph
118 }
119
120 pub fn edge_weights(&self) -> &[W] {
122 &self.edge_weights
123 }
124
125 pub fn source(&self) -> usize {
127 self.source
128 }
129
130 pub fn sink(&self) -> usize {
132 self.sink
133 }
134
135 pub fn size_bound(&self) -> usize {
137 self.size_bound
138 }
139
140 pub fn num_vertices(&self) -> usize {
142 self.graph.num_vertices()
143 }
144
145 pub fn num_edges(&self) -> usize {
147 self.graph.num_edges()
148 }
149}
150
151impl<G, W> Problem for MinimumCutIntoBoundedSets<G, W>
152where
153 G: Graph + crate::variant::VariantParam,
154 W: WeightElement + crate::variant::VariantParam,
155{
156 const NAME: &'static str = "MinimumCutIntoBoundedSets";
157 type Value = Min<W::Sum>;
158
159 fn variant() -> Vec<(&'static str, &'static str)> {
160 crate::variant_params![G, W]
161 }
162
163 fn dims(&self) -> Vec<usize> {
164 vec![2; self.graph.num_vertices()]
165 }
166
167 fn evaluate(&self, config: &[usize]) -> Min<W::Sum> {
168 let n = self.graph.num_vertices();
169 if config.len() != n {
170 return Min(None);
171 }
172
173 if config[self.source] != 0 || config[self.sink] != 1 {
175 return Min(None);
176 }
177
178 let count_v1 = config.iter().filter(|&&x| x == 0).count();
180 let count_v2 = config.iter().filter(|&&x| x == 1).count();
181 if count_v1 > self.size_bound || count_v2 > self.size_bound {
182 return Min(None);
183 }
184
185 let mut cut_weight = W::Sum::zero();
187 for ((u, v), weight) in self.graph.edges().iter().zip(self.edge_weights.iter()) {
188 if config[*u] != config[*v] {
189 cut_weight += weight.to_sum();
190 }
191 }
192
193 Min(Some(cut_weight))
194 }
195}
196
197#[cfg(feature = "example-db")]
198pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
199 vec![crate::example_db::specs::ModelExampleSpec {
200 id: "minimum_cut_into_bounded_sets_i32",
201 instance: Box::new(MinimumCutIntoBoundedSets::new(
202 SimpleGraph::new(
203 8,
204 vec![
205 (0, 1),
206 (0, 2),
207 (1, 2),
208 (1, 3),
209 (2, 4),
210 (3, 5),
211 (3, 6),
212 (4, 5),
213 (4, 6),
214 (5, 7),
215 (6, 7),
216 (5, 6),
217 ],
218 ),
219 vec![2, 3, 1, 4, 2, 1, 3, 2, 1, 2, 3, 1],
220 0,
221 7,
222 5,
223 )),
224 optimal_config: vec![0, 0, 0, 0, 1, 1, 1, 1],
226 optimal_value: serde_json::json!(6),
227 }]
228}
229
230crate::declare_variants! {
231 default MinimumCutIntoBoundedSets<SimpleGraph, i32> => "2^num_vertices",
232}
233
234#[cfg(test)]
235#[path = "../../unit_tests/models/graph/minimum_cut_into_bounded_sets.rs"]
236mod tests;