problemreductions/models/graph/
max_cut.rs1use crate::registry::{FieldInfo, ProblemSchemaEntry};
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 module_path: module_path!(),
17 description: "Find maximum weight cut in a graph",
18 fields: &[
19 FieldInfo { name: "graph", type_name: "G", description: "The graph with edge weights" },
20 FieldInfo { name: "edge_weights", type_name: "Vec<W>", description: "Edge weights w: E -> R" },
21 ],
22 }
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct MaxCut<G, W> {
68 graph: G,
70 edge_weights: Vec<W>,
72}
73
74impl<G: Graph, W: Clone + Default> MaxCut<G, W> {
75 pub fn new(graph: G, edge_weights: Vec<W>) -> Self {
81 assert_eq!(
82 edge_weights.len(),
83 graph.num_edges(),
84 "edge_weights length must match num_edges"
85 );
86 Self {
87 graph,
88 edge_weights,
89 }
90 }
91
92 pub fn unweighted(graph: G) -> Self
94 where
95 W: From<i32>,
96 {
97 let edge_weights = vec![W::from(1); graph.num_edges()];
98 Self {
99 graph,
100 edge_weights,
101 }
102 }
103
104 pub fn graph(&self) -> &G {
106 &self.graph
107 }
108
109 pub fn edges(&self) -> Vec<(usize, usize, W)> {
111 self.graph
112 .edges()
113 .into_iter()
114 .zip(self.edge_weights.iter())
115 .map(|((u, v), w)| (u, v, w.clone()))
116 .collect()
117 }
118
119 pub fn edge_weight_by_index(&self, idx: usize) -> Option<&W> {
121 self.edge_weights.get(idx)
122 }
123
124 pub fn edge_weight(&self, u: usize, v: usize) -> Option<&W> {
126 for (idx, (eu, ev)) in self.graph.edges().iter().enumerate() {
128 if (*eu == u && *ev == v) || (*eu == v && *ev == u) {
129 return self.edge_weights.get(idx);
130 }
131 }
132 None
133 }
134
135 pub fn edge_weights(&self) -> Vec<W> {
137 self.edge_weights.clone()
138 }
139
140 pub fn cut_size(&self, config: &[usize]) -> W::Sum
142 where
143 W: WeightElement,
144 {
145 let partition: Vec<bool> = config.iter().map(|&c| c != 0).collect();
146 cut_size(&self.graph, &self.edge_weights, &partition)
147 }
148}
149
150impl<G: Graph, W: WeightElement> MaxCut<G, W> {
151 pub fn num_vertices(&self) -> usize {
153 self.graph().num_vertices()
154 }
155
156 pub fn num_edges(&self) -> usize {
158 self.graph().num_edges()
159 }
160}
161
162impl<G, W> Problem for MaxCut<G, W>
163where
164 G: Graph + crate::variant::VariantParam,
165 W: WeightElement + crate::variant::VariantParam,
166{
167 const NAME: &'static str = "MaxCut";
168 type Metric = SolutionSize<W::Sum>;
169
170 fn variant() -> Vec<(&'static str, &'static str)> {
171 crate::variant_params![G, W]
172 }
173
174 fn dims(&self) -> Vec<usize> {
175 vec![2; self.graph.num_vertices()]
176 }
177
178 fn evaluate(&self, config: &[usize]) -> SolutionSize<W::Sum> {
179 let partition: Vec<bool> = config.iter().map(|&c| c != 0).collect();
181 SolutionSize::Valid(cut_size(&self.graph, &self.edge_weights, &partition))
182 }
183}
184
185impl<G, W> OptimizationProblem for MaxCut<G, W>
186where
187 G: Graph + crate::variant::VariantParam,
188 W: WeightElement + crate::variant::VariantParam,
189{
190 type Value = W::Sum;
191
192 fn direction(&self) -> Direction {
193 Direction::Maximize
194 }
195}
196
197pub(crate) fn cut_size<G, W>(graph: &G, edge_weights: &[W], partition: &[bool]) -> W::Sum
204where
205 G: Graph,
206 W: WeightElement,
207{
208 let mut total = W::Sum::zero();
209 for ((u, v), weight) in graph.edges().iter().zip(edge_weights.iter()) {
210 if *u < partition.len() && *v < partition.len() && partition[*u] != partition[*v] {
211 total += weight.to_sum();
212 }
213 }
214 total
215}
216
217crate::declare_variants! {
218 MaxCut<SimpleGraph, i32> => "2^(2.372 * num_vertices / 3)",
219}
220
221#[cfg(test)]
222#[path = "../../unit_tests/models/graph/max_cut.rs"]
223mod tests;