rust_igraph/algorithms/
max_cut.rs1use crate::core::{Graph, IgraphResult, VertexId};
13
14#[derive(Debug, Clone)]
16pub struct MaxCutResult {
17 pub partition: Vec<bool>,
20 pub cut_value: usize,
22}
23
24#[allow(clippy::cast_possible_truncation)]
46pub fn cut_value(graph: &Graph, partition: &[bool]) -> IgraphResult<usize> {
47 let ecount = graph.ecount();
48 let mut value = 0usize;
49
50 for eid in 0..ecount as u32 {
51 let (u, v) = graph.edge(eid)?;
52 if partition[u as usize] != partition[v as usize] {
53 value = value.saturating_add(1);
54 }
55 }
56
57 Ok(value)
58}
59
60#[allow(clippy::cast_possible_truncation)]
86pub fn maximum_cut(graph: &Graph) -> IgraphResult<MaxCutResult> {
87 let n = graph.vcount();
88 let directed = graph.is_directed();
89
90 if n == 0 {
91 return Ok(MaxCutResult {
92 partition: Vec::new(),
93 cut_value: 0,
94 });
95 }
96
97 let mut partition = vec![false; n as usize];
98
99 let mut adj: Vec<Vec<VertexId>> = Vec::with_capacity(n as usize);
101 for v in 0..n {
102 let mut neighbors = graph.neighbors(v)?;
103 if directed {
104 let in_n = graph.in_neighbors_vec(v)?;
105 for w in in_n {
106 if !neighbors.contains(&w) {
107 neighbors.push(w);
108 }
109 }
110 }
111 adj.push(neighbors);
112 }
113
114 for v in 0..n {
118 let mut count_in_s = 0u32;
119 let mut count_in_complement = 0u32;
120
121 for &w in &adj[v as usize] {
122 if w == v || w >= v {
123 continue;
124 }
125 if partition[w as usize] {
126 count_in_s = count_in_s.saturating_add(1);
127 } else {
128 count_in_complement = count_in_complement.saturating_add(1);
129 }
130 }
131
132 partition[v as usize] = count_in_complement > count_in_s;
133 }
134
135 let value = cut_value(graph, &partition)?;
136
137 Ok(MaxCutResult {
138 partition,
139 cut_value: value,
140 })
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn empty_graph() {
149 let g = Graph::with_vertices(0);
150 let r = maximum_cut(&g).unwrap();
151 assert!(r.partition.is_empty());
152 assert_eq!(r.cut_value, 0);
153 }
154
155 #[test]
156 fn no_edges() {
157 let g = Graph::with_vertices(5);
158 let r = maximum_cut(&g).unwrap();
159 assert_eq!(r.cut_value, 0);
160 }
161
162 #[test]
163 fn single_edge() {
164 let mut g = Graph::with_vertices(2);
165 g.add_edge(0, 1).unwrap();
166 let r = maximum_cut(&g).unwrap();
167 assert_eq!(r.cut_value, 1);
168 assert_ne!(r.partition[0], r.partition[1]);
169 }
170
171 #[test]
172 fn path_4() {
173 let mut g = Graph::with_vertices(4);
174 g.add_edge(0, 1).unwrap();
175 g.add_edge(1, 2).unwrap();
176 g.add_edge(2, 3).unwrap();
177 let r = maximum_cut(&g).unwrap();
178 assert_eq!(r.cut_value, cut_value(&g, &r.partition).unwrap());
179 assert!(r.cut_value >= 1); }
181
182 #[test]
183 fn triangle() {
184 let mut g = Graph::with_vertices(3);
185 g.add_edge(0, 1).unwrap();
186 g.add_edge(1, 2).unwrap();
187 g.add_edge(2, 0).unwrap();
188 let r = maximum_cut(&g).unwrap();
189 assert!(r.cut_value >= 1); assert_eq!(r.cut_value, 2); }
192
193 #[test]
194 fn complete_k4() {
195 let mut g = Graph::with_vertices(4);
196 for u in 0..4u32 {
197 for v in (u + 1)..4 {
198 g.add_edge(u, v).unwrap();
199 }
200 }
201 let r = maximum_cut(&g).unwrap();
202 assert!(r.cut_value >= 3); assert_eq!(r.cut_value, cut_value(&g, &r.partition).unwrap());
204 }
205
206 #[test]
207 fn bipartite_k22() {
208 let mut g = Graph::with_vertices(4);
209 g.add_edge(0, 2).unwrap();
210 g.add_edge(0, 3).unwrap();
211 g.add_edge(1, 2).unwrap();
212 g.add_edge(1, 3).unwrap();
213 let r = maximum_cut(&g).unwrap();
214 assert!(r.cut_value >= 2); assert_eq!(r.cut_value, 4);
217 }
218
219 #[test]
220 fn star_graph() {
221 let mut g = Graph::with_vertices(5);
222 for i in 1..5u32 {
223 g.add_edge(0, i).unwrap();
224 }
225 let r = maximum_cut(&g).unwrap();
226 assert!(r.cut_value >= 2); assert_eq!(r.cut_value, 4); }
229
230 #[test]
231 fn self_loop_ignored() {
232 let mut g = Graph::with_vertices(2);
233 g.add_edge(0, 0).unwrap();
234 g.add_edge(0, 1).unwrap();
235 let r = maximum_cut(&g).unwrap();
236 assert_eq!(r.cut_value, 1); }
238
239 #[test]
240 fn directed_graph() {
241 let mut g = Graph::new(3, true).unwrap();
242 g.add_edge(0, 1).unwrap();
243 g.add_edge(1, 2).unwrap();
244 g.add_edge(2, 0).unwrap();
245 let r = maximum_cut(&g).unwrap();
246 assert!(r.cut_value >= 1);
247 assert_eq!(r.cut_value, cut_value(&g, &r.partition).unwrap());
248 }
249
250 #[test]
251 fn cut_value_all_same_side() {
252 let mut g = Graph::with_vertices(3);
253 g.add_edge(0, 1).unwrap();
254 g.add_edge(1, 2).unwrap();
255 let partition = vec![true, true, true];
256 assert_eq!(cut_value(&g, &partition).unwrap(), 0);
257 }
258
259 #[test]
260 fn cut_value_alternating() {
261 let mut g = Graph::with_vertices(4);
262 g.add_edge(0, 1).unwrap();
263 g.add_edge(1, 2).unwrap();
264 g.add_edge(2, 3).unwrap();
265 let partition = vec![true, false, true, false];
266 assert_eq!(cut_value(&g, &partition).unwrap(), 3);
267 }
268
269 #[test]
270 fn guarantee_half_edges() {
271 let mut g = Graph::with_vertices(6);
273 for i in 0..6u32 {
274 g.add_edge(i, (i + 1) % 6).unwrap();
275 }
276 let r = maximum_cut(&g).unwrap();
277 assert!(r.cut_value >= 3);
278 }
279
280 #[test]
281 fn parallel_edges() {
282 let mut g = Graph::with_vertices(2);
283 g.add_edge(0, 1).unwrap();
284 g.add_edge(0, 1).unwrap();
285 g.add_edge(0, 1).unwrap();
286 let r = maximum_cut(&g).unwrap();
287 assert_eq!(r.cut_value, 3); }
289}