problemreductions/models/graph/
max_cut.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry, VariantDimension};
7use crate::topology::{Graph, SimpleGraph};
8use crate::traits::{OptimizationProblem, Problem};
9use crate::types::{Direction, SolutionSize, WeightElement};
10use num_traits::Zero;
11use serde::{Deserialize, Serialize};
12
13inventory::submit! {
14 ProblemSchemaEntry {
15 name: "MaxCut",
16 display_name: "Max Cut",
17 aliases: &[],
18 dimensions: &[
19 VariantDimension::new("graph", "SimpleGraph", &["SimpleGraph"]),
20 VariantDimension::new("weight", "i32", &["i32"]),
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 Metric = SolutionSize<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]) -> SolutionSize<W::Sum> {
185 let partition: Vec<bool> = config.iter().map(|&c| c != 0).collect();
187 SolutionSize::Valid(cut_size(&self.graph, &self.edge_weights, &partition))
188 }
189}
190
191impl<G, W> OptimizationProblem for MaxCut<G, W>
192where
193 G: Graph + crate::variant::VariantParam,
194 W: WeightElement + crate::variant::VariantParam,
195{
196 type Value = W::Sum;
197
198 fn direction(&self) -> Direction {
199 Direction::Maximize
200 }
201}
202
203pub(crate) fn cut_size<G, W>(graph: &G, edge_weights: &[W], partition: &[bool]) -> W::Sum
210where
211 G: Graph,
212 W: WeightElement,
213{
214 let mut total = W::Sum::zero();
215 for ((u, v), weight) in graph.edges().iter().zip(edge_weights.iter()) {
216 if *u < partition.len() && *v < partition.len() && partition[*u] != partition[*v] {
217 total += weight.to_sum();
218 }
219 }
220 total
221}
222
223crate::declare_variants! {
224 default opt MaxCut<SimpleGraph, i32> => "2^(2.372 * num_vertices / 3)",
225}
226
227#[cfg(feature = "example-db")]
228pub(crate) fn canonical_model_example_specs() -> Vec<crate::example_db::specs::ModelExampleSpec> {
229 vec![crate::example_db::specs::ModelExampleSpec {
230 id: "max_cut_simplegraph_i32",
231 instance: Box::new(MaxCut::<_, i32>::unweighted(SimpleGraph::new(
232 5,
233 vec![(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)],
234 ))),
235 optimal_config: vec![1, 0, 0, 1, 0],
236 optimal_value: serde_json::json!({"Valid": 5}),
237 }]
238}
239
240#[cfg(test)]
241#[path = "../../unit_tests/models/graph/max_cut.rs"]
242mod tests;