1use crate::graph_types::SimpleGraph;
7use crate::traits::Problem;
8use crate::types::{EnergyMode, ProblemSize, SolutionSize};
9use petgraph::graph::{NodeIndex, UnGraph};
10use petgraph::visit::EdgeRef;
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct MaxCut<W = i32> {
48 graph: UnGraph<(), W>,
50}
51
52impl<W: Clone + Default> MaxCut<W> {
53 pub fn new(num_vertices: usize, edges: Vec<(usize, usize, W)>) -> Self {
59 let mut graph = UnGraph::new_undirected();
60 for _ in 0..num_vertices {
61 graph.add_node(());
62 }
63 for (u, v, w) in edges {
64 graph.add_edge(NodeIndex::new(u), NodeIndex::new(v), w);
65 }
66 Self { graph }
67 }
68
69 pub fn unweighted(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self
71 where
72 W: From<i32>,
73 {
74 Self::new(
75 num_vertices,
76 edges.into_iter().map(|(u, v)| (u, v, W::from(1))).collect(),
77 )
78 }
79
80 pub fn num_vertices(&self) -> usize {
82 self.graph.node_count()
83 }
84
85 pub fn num_edges(&self) -> usize {
87 self.graph.edge_count()
88 }
89
90 pub fn edges(&self) -> Vec<(usize, usize, W)> {
92 self.graph
93 .edge_references()
94 .map(|e| (e.source().index(), e.target().index(), e.weight().clone()))
95 .collect()
96 }
97
98 pub fn edge_weight(&self, u: usize, v: usize) -> Option<&W> {
100 self.graph
101 .find_edge(NodeIndex::new(u), NodeIndex::new(v))
102 .map(|e| self.graph.edge_weight(e).unwrap())
103 }
104
105 pub fn with_weights(num_vertices: usize, edges: Vec<(usize, usize)>, weights: Vec<W>) -> Self {
107 assert_eq!(edges.len(), weights.len(), "edges and weights must have same length");
108 let mut graph = UnGraph::new_undirected();
109 for _ in 0..num_vertices {
110 graph.add_node(());
111 }
112 for ((u, v), w) in edges.into_iter().zip(weights.into_iter()) {
113 graph.add_edge(NodeIndex::new(u), NodeIndex::new(v), w);
114 }
115 Self { graph }
116 }
117
118 pub fn edge_weights(&self) -> Vec<W> {
120 self.graph
121 .edge_references()
122 .map(|e| e.weight().clone())
123 .collect()
124 }
125}
126
127impl<W> Problem for MaxCut<W>
128where
129 W: Clone + Default + PartialOrd + num_traits::Num + num_traits::Zero + std::ops::AddAssign + 'static,
130{
131 const NAME: &'static str = "MaxCut";
132 type GraphType = SimpleGraph;
133 type Weight = W;
134 type Size = W;
135
136 fn num_variables(&self) -> usize {
137 self.graph.node_count()
138 }
139
140 fn num_flavors(&self) -> usize {
141 2 }
143
144 fn problem_size(&self) -> ProblemSize {
145 ProblemSize::new(vec![
146 ("num_vertices", self.graph.node_count()),
147 ("num_edges", self.graph.edge_count()),
148 ])
149 }
150
151 fn energy_mode(&self) -> EnergyMode {
152 EnergyMode::LargerSizeIsBetter }
154
155 fn solution_size(&self, config: &[usize]) -> SolutionSize<Self::Size> {
156 let cut_weight = compute_cut_weight(&self.graph, config);
157 SolutionSize::valid(cut_weight)
159 }
160}
161
162fn compute_cut_weight<W>(graph: &UnGraph<(), W>, config: &[usize]) -> W
164where
165 W: Clone + num_traits::Zero + std::ops::AddAssign,
166{
167 let mut total = W::zero();
168 for edge in graph.edge_references() {
169 let u = edge.source().index();
170 let v = edge.target().index();
171 let u_side = config.get(u).copied().unwrap_or(0);
172 let v_side = config.get(v).copied().unwrap_or(0);
173 if u_side != v_side {
174 total += edge.weight().clone();
175 }
176 }
177 total
178}
179
180pub fn cut_size<W>(edges: &[(usize, usize, W)], partition: &[bool]) -> W
186where
187 W: Clone + num_traits::Zero + std::ops::AddAssign,
188{
189 let mut total = W::zero();
190 for (u, v, w) in edges {
191 if *u < partition.len() && *v < partition.len() && partition[*u] != partition[*v] {
192 total += w.clone();
193 }
194 }
195 total
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use crate::solvers::{BruteForce, Solver};
202
203 #[test]
204 fn test_maxcut_creation() {
205 let problem = MaxCut::new(4, vec![(0, 1, 1), (1, 2, 2), (2, 3, 3)]);
206 assert_eq!(problem.num_vertices(), 4);
207 assert_eq!(problem.num_edges(), 3);
208 assert_eq!(problem.num_variables(), 4);
209 assert_eq!(problem.num_flavors(), 2);
210 }
211
212 #[test]
213 fn test_maxcut_unweighted() {
214 let problem = MaxCut::<i32>::unweighted(3, vec![(0, 1), (1, 2)]);
215 assert_eq!(problem.num_edges(), 2);
216 }
217
218 #[test]
219 fn test_solution_size() {
220 let problem = MaxCut::new(3, vec![(0, 1, 1), (1, 2, 2), (0, 2, 3)]);
221
222 let sol = problem.solution_size(&[0, 0, 0]);
224 assert_eq!(sol.size, 0);
225 assert!(sol.is_valid);
226
227 let sol = problem.solution_size(&[0, 1, 1]);
229 assert_eq!(sol.size, 4);
230
231 let sol = problem.solution_size(&[0, 1, 0]);
233 assert_eq!(sol.size, 3);
234 }
235
236 #[test]
237 fn test_brute_force_triangle() {
238 let problem = MaxCut::<i32>::unweighted(3, vec![(0, 1), (1, 2), (0, 2)]);
240 let solver = BruteForce::new();
241
242 let solutions = solver.find_best(&problem);
243 for sol in &solutions {
244 let size = problem.solution_size(sol);
245 assert_eq!(size.size, 2);
246 }
247 }
248
249 #[test]
250 fn test_brute_force_path() {
251 let problem = MaxCut::<i32>::unweighted(3, vec![(0, 1), (1, 2)]);
253 let solver = BruteForce::new();
254
255 let solutions = solver.find_best(&problem);
256 for sol in &solutions {
257 let size = problem.solution_size(sol);
258 assert_eq!(size.size, 2);
259 }
260 }
261
262 #[test]
263 fn test_brute_force_weighted() {
264 let problem = MaxCut::new(3, vec![(0, 1, 10), (1, 2, 1)]);
266 let solver = BruteForce::new();
267
268 let solutions = solver.find_best(&problem);
269 for sol in &solutions {
271 let size = problem.solution_size(sol);
272 assert_eq!(size.size, 11);
273 }
274 }
275
276 #[test]
277 fn test_cut_size_function() {
278 let edges = vec![(0, 1, 1), (1, 2, 2), (0, 2, 3)];
279
280 assert_eq!(cut_size(&edges, &[false, true, true]), 4); assert_eq!(cut_size(&edges, &[false, false, true]), 5); assert_eq!(cut_size(&edges, &[false, false, false]), 0);
288 }
289
290 #[test]
291 fn test_edge_weight() {
292 let problem = MaxCut::new(3, vec![(0, 1, 5), (1, 2, 10)]);
293 assert_eq!(problem.edge_weight(0, 1), Some(&5));
294 assert_eq!(problem.edge_weight(1, 2), Some(&10));
295 assert_eq!(problem.edge_weight(0, 2), None);
296 }
297
298 #[test]
299 fn test_edges() {
300 let problem = MaxCut::new(3, vec![(0, 1, 1), (1, 2, 2)]);
301 let edges = problem.edges();
302 assert_eq!(edges.len(), 2);
303 }
304
305 #[test]
306 fn test_energy_mode() {
307 let problem = MaxCut::<i32>::unweighted(2, vec![(0, 1)]);
308 assert!(problem.energy_mode().is_maximization());
309 }
310
311 #[test]
312 fn test_empty_graph() {
313 let problem = MaxCut::<i32>::unweighted(3, vec![]);
314 let solver = BruteForce::new();
315
316 let solutions = solver.find_best(&problem);
317 assert!(!solutions.is_empty());
319 for sol in &solutions {
320 assert_eq!(problem.solution_size(sol).size, 0);
321 }
322 }
323
324 #[test]
325 fn test_single_edge() {
326 let problem = MaxCut::new(2, vec![(0, 1, 5)]);
327 let solver = BruteForce::new();
328
329 let solutions = solver.find_best(&problem);
330 assert_eq!(solutions.len(), 2); for sol in &solutions {
333 assert_eq!(problem.solution_size(sol).size, 5);
334 }
335 }
336
337 #[test]
338 fn test_complete_graph_k4() {
339 let problem =
341 MaxCut::<i32>::unweighted(4, vec![(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
342 let solver = BruteForce::new();
343
344 let solutions = solver.find_best(&problem);
345 for sol in &solutions {
347 assert_eq!(problem.solution_size(sol).size, 4);
348 }
349 }
350
351 #[test]
352 fn test_bipartite_graph() {
353 let problem = MaxCut::<i32>::unweighted(4, vec![(0, 2), (0, 3), (1, 2), (1, 3)]);
355 let solver = BruteForce::new();
356
357 let solutions = solver.find_best(&problem);
358 for sol in &solutions {
360 assert_eq!(problem.solution_size(sol).size, 4);
361 }
362 }
363
364 #[test]
365 fn test_symmetry() {
366 let problem = MaxCut::<i32>::unweighted(3, vec![(0, 1), (1, 2), (0, 2)]);
368
369 let sol1 = problem.solution_size(&[0, 1, 1]);
370 let sol2 = problem.solution_size(&[1, 0, 0]); assert_eq!(sol1.size, sol2.size);
372 }
373}