problemreductions/models/graph/
max_cut.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::Problem;
9use crate::types::{Max, One, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MaxCut",
16 display_name: "Max Cut",
17 aliases: &["GraphPartitioning", "MaximumBipartiteSubgraph"],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 VariantDimension::new("weight", "i32", &["i32", "One"]),
21 ],
22 module_path: module_path!(),
23 description: "Find maximum weight cut in a graph",
24 fields: &[
25 FieldInfo { name: "graph", type_name: "G", description: "The graph with edge weights" },
26 FieldInfo { name: "edge_weights", type_name: "Vec<W>", description: "Edge weights w: E -> R" },
27 ],
28 }
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct MaxCut<G, W> {
74 graph: G,
76 edge_weights: Vec<W>,
78}
79
80impl<G: Graph, W: Clone + Default> MaxCut<G, W> {
81 pub fn new(graph: G, edge_weights: Vec<W>) -> Self {
87 assert_eq!(
88 edge_weights.len(),
89 graph.num_edges(),
90 "edge_weights length must match num_edges"
91 );
92 Self {
93 graph,
94 edge_weights,
95 }
96 }
97
98 pub fn unweighted(graph: G) -> Self
100 where
101 W: From<i32>,
102 {
103 let edge_weights = vec![W::from(1); graph.num_edges()];
104 Self {
105 graph,
106 edge_weights,
107 }
108 }
109
110 pub fn graph(&self) -> &G {
112 &self.graph
113 }
114
115 pub fn edges(&self) -> Vec<(usize, usize, W)> {
117 self.graph
118 .edges()
119 .into_iter()
120 .zip(self.edge_weights.iter())
121 .map(|((u, v), w)| (u, v, w.clone()))
122 .collect()
123 }
124
125 pub fn edge_weight_by_index(&self, idx: usize) -> Option<&W> {
127 self.edge_weights.get(idx)
128 }
129
130 pub fn edge_weight(&self, u: usize, v: usize) -> Option<&W> {
132 for (idx, (eu, ev)) in self.graph.edges().iter().enumerate() {
134 if (*eu == u && *ev == v) || (*eu == v && *ev == u) {
135 return self.edge_weights.get(idx);
136 }
137 }
138 None
139 }
140
141 pub fn edge_weights(&self) -> Vec<W> {
143 self.edge_weights.clone()
144 }
145
146 pub fn cut_size(&self, config: &[usize]) -> W::Sum
148 where
149 W: WeightElement,
150 {
151 let partition: Vec<bool> = config.iter().map(|&c| c != 0).collect();
152 cut_size(&self.graph, &self.edge_weights, &partition)
153 }
154}
155
156impl<G: Graph, W: WeightElement> MaxCut<G, W> {
157 pub fn num_vertices(&self) -> usize {
159 self.graph().num_vertices()
160 }
161
162 pub fn num_edges(&self) -> usize {
164 self.graph().num_edges()
165 }
166}
167
168impl<G, W> Problem for MaxCut<G, W>
169where
170 G: Graph + crate::variant::VariantParam,
171 W: WeightElement + crate::variant::VariantParam,
172{
173 const NAME: &'static str = "MaxCut";
174 type Value = Max<W::Sum>;
175
176 fn variant() -> Vec<(&'static str, &'static str)> {
177 crate::variant_params![G, W]
178 }
179
180 fn dims(&self) -> Vec<usize> {
181 vec![2; self.graph.num_vertices()]
182 }
183
184 fn evaluate(&self, config: &[usize]) -> Max<W::Sum> {
185 let partition: Vec<bool> = config.iter().map(|&c| c != 0).collect();
187 Max(Some(cut_size(&self.graph, &self.edge_weights, &partition)))
188 }
189}
190
191pub(crate) fn cut_size<G, W>(graph: &G, edge_weights: &[W], partition: &[bool]) -> W::Sum
198where
199 G: Graph,
200 W: WeightElement,
201{
202 let mut total = W::Sum::zero();
203 for ((u, v), weight) in graph.edges().iter().zip(edge_weights.iter()) {
204 if *u < partition.len() && *v < partition.len() && partition[*u] != partition[*v] {
205 total += weight.to_sum();
206 }
207 }
208 total
209}
210
211crate::declare_variants! {
212 default MaxCut<SimpleGraph, i32> => "2^(2.372 * num_vertices / 3)",
213 MaxCut<SimpleGraph, One> => "2^(0.7907 * num_vertices)",
214}
215
216#[cfg(feature = "example-db")]
217pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
218 vec![
219 crate::example_db::specs::ModelExampleSpec {
220 id: "max_cut_simplegraph_i32",
221 instance: Box::new(MaxCut::<_, i32>::unweighted(SimpleGraph::new(
222 5,
223 vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)],
224 ))),
225 optimal_config: vec![1, 0, 0, 1, 0],
226 optimal_value: serde_json::json!(5),
227 },
228 crate::example_db::specs::ModelExampleSpec {
229 id: "max_cut_simplegraph_one",
230 instance: Box::new(MaxCut::new(
231 SimpleGraph::new(
232 5,
233 vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 4), (2, 3), (3, 4)],
234 ),
235 vec![One; 7],
236 )),
237 optimal_config: vec![0, 1, 0, 1, 0],
238 optimal_value: serde_json::json!(6),
239 },
240 ]
241}
242
243#[cfg(test)]
244#[path = "../../unit_tests/models/graph/max_cut.rs"]
245mod tests;