Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Stoer_Wagner.H
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
105# ifndef STOER_WAGNER_H
106# define STOER_WAGNER_H
107
108# include <limits>
109# include <vector>
110# include <queue>
111# include <functional>
112# include <htlist.H>
113# include <tpl_sgraph.H>
114# include <tpl_dynArray.H>
115# include <tpl_graph_utils.H>
116# include <ah-errors.H>
117
118namespace Aleph
119{
200 template <class GT,
201 class Distance = Dft_Dist<GT>,
202 class SA = Dft_Show_Arc<GT>>
204 {
205 public:
206 using Node = typename GT::Node;
207 using Arc = typename GT::Arc;
209
210 private:
213
214 // Internal representation for contraction
215 struct SWNode
216 {
217 DynList<Node*> members; // Original nodes in this super-node
218 size_t id; // Index for adjacency matrix
219 bool merged; // True if merged into another node
220 };
221
222 // Priority queue entry
223 struct PQEntry
224 {
225 size_t node_id;
227
228 bool operator<(const PQEntry& other) const
229 {
230 return priority < other.priority; // Max-heap: higher priority first
231 }
232 };
233
234 // Adjacency matrix for weights (dense representation for efficiency)
235 std::vector<std::vector<Weight>> adj;
236 std::vector<SWNode> nodes;
238
239 // Initialize internal structures from graph
241 {
242 const size_t n = g.get_num_nodes();
243 adj.assign(n, std::vector<Weight>(n, Weight(0)));
244 nodes.resize(n);
245 active_count = n;
246
247 // Assign IDs to nodes
248 size_t id = 0;
249 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
250 {
251 auto p = it.get_curr();
252 NODE_COUNTER(p) = id;
253 nodes[id].members.append(p);
254 nodes[id].id = id;
255 nodes[id].merged = false;
256 ++id;
257 }
258
259 // Build adjacency matrix
260 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
261 {
262 auto a = it.get_curr();
263 size_t src_id = NODE_COUNTER(g.get_src_node(a));
264 size_t tgt_id = NODE_COUNTER(g.get_tgt_node(a));
265 Weight w = distance(a);
266
267 adj[src_id][tgt_id] += w;
268 adj[tgt_id][src_id] += w; // Undirected
269 }
270 }
271
272 // Maximum Adjacency Search: returns (s, t, cut-of-phase weight)
273 std::tuple<size_t, size_t, Weight> minimum_cut_phase()
274 {
275 const size_t n = nodes.size();
276
277 // Find first active node to start
278 size_t start = 0;
279 while (start < n && nodes[start].merged)
280 ++start;
281
282 if (start >= n)
283 return {0, 0, Weight(0)};
284
285 // Priority (sum of weights to nodes in A)
286 std::vector<Weight> key(n, Weight(0));
287 std::vector<bool> in_A(n, false);
288
289 // Use priority queue (max-heap)
290 std::priority_queue<PQEntry> pq;
291
292 // Initialize: add starting node to A
293 in_A[start] = true;
294 size_t last = start;
295 size_t second_last = start;
296
297 // Update priorities for neighbors of start
298 for (size_t j = 0; j < n; ++j)
299 {
300 if (!nodes[j].merged && !in_A[j] && adj[start][j] > Weight(0))
301 {
302 key[j] = adj[start][j];
303 pq.push({j, key[j]});
304 }
305 }
306
307 // Add remaining nodes one by one
308 for (size_t count = 1; count < active_count; ++count)
309 {
310 // Find node with maximum key not in A
311 size_t next = n;
312 while (!pq.empty())
313 {
314 auto top = pq.top();
315 pq.pop();
316
317 if (!in_A[top.node_id] && !nodes[top.node_id].merged &&
318 top.priority == key[top.node_id])
319 {
320 next = top.node_id;
321 break;
322 }
323 }
324
325 if (next >= n)
326 {
327 // Graph is disconnected - find any unvisited node
328 for (size_t j = 0; j < n; ++j)
329 {
330 if (!nodes[j].merged && !in_A[j])
331 {
332 next = j;
333 break;
334 }
335 }
336 }
337
338 if (next >= n)
339 break;
340
341 // Add next to A
342 second_last = last;
343 last = next;
344 in_A[next] = true;
345
346 // Update keys for neighbors of next
347 for (size_t j = 0; j < n; ++j)
348 {
349 if (!nodes[j].merged && !in_A[j] && adj[next][j] > Weight(0))
350 {
351 key[j] += adj[next][j];
352 pq.push({j, key[j]});
353 }
354 }
355 }
356
357 // Cut-of-phase: weight of edges from last to A
358 Weight cut_weight = key[last];
359
360 return {second_last, last, cut_weight};
361 }
362
363 // Merge node t into node s
364 void merge_nodes(size_t s, size_t t)
365 {
366 const size_t n = nodes.size();
367
368 // Move members from t to s
369 nodes[s].members.concat(nodes[t].members);
370 nodes[t].merged = true;
371
372 // Update adjacency: combine edges
373 for (size_t j = 0; j < n; ++j)
374 {
375 if (j != s && j != t && !nodes[j].merged)
376 {
377 adj[s][j] += adj[t][j];
378 adj[j][s] = adj[s][j];
379 }
380 }
381
382 // Clear t's edges
383 for (size_t j = 0; j < n; ++j)
384 {
385 adj[t][j] = Weight(0);
386 adj[j][t] = Weight(0);
387 }
388
389 --active_count;
390 }
391
392 public:
395
402
421 DynList<Node*>& vs,
423 DynList<Arc*>& cut)
424 {
425 const size_t n = g.get_num_nodes();
426 ah_domain_error_if(n < 2) << "Graph must have at least 2 nodes";
427
428 vs.empty();
429 vt.empty();
430 cut.empty();
431
433
434 Weight best_cut = std::numeric_limits<Weight>::max();
435
436 // Main loop: n-1 phases
437 while (active_count > 1)
438 {
439 auto [s, t, cut_weight] = minimum_cut_phase();
440 (void)s; // Suppress unused warning
441
442 if (cut_weight < best_cut)
444
445 // Merge s and t
446 merge_nodes(s, t);
447 }
448
449 // Reconstruct partitions
450 // The best cut separates nodes[best_t].members from the rest
451 // But nodes have been merged, so we need to track this differently
452
453 // Rebuild: run algorithm again but stop at best cut
455
458
459 while (active_count > 1)
460 {
461 auto [s, t, cut_weight] = minimum_cut_phase();
462
464 {
465 // This is our target cut - save t's side
466 for (auto p : nodes[t].members)
467 cut_side.append(p);
468 }
469
470 merge_nodes(s, t);
471 }
472
473 // Build partitions
474 for (auto p : cut_side)
475 vt.append(p);
476
477 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
478 {
479 auto p = it.get_curr();
480 if (!cut_side.exists([p](Node* q) { return p == q; }))
481 vs.append(p);
482 }
483
484 // Find cut arcs
485 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
486 {
487 auto a = it.get_curr();
488 auto src = g.get_src_node(a);
489 auto tgt = g.get_tgt_node(a);
490
491 bool src_in_vs = vs.exists([src](Node* p) { return p == src; });
492 bool tgt_in_vs = vs.exists([tgt](Node* p) { return p == tgt; });
493
494 if (src_in_vs != tgt_in_vs)
495 cut.append(a);
496 }
497
498 return best_cut;
499 }
500
510 {
511 const size_t n = g.get_num_nodes();
512 if (n < 2)
513 return Weight(0);
514
516
517 Weight best_cut = std::numeric_limits<Weight>::max();
518
519 while (active_count > 1)
520 {
521 auto [s, t, cut_weight] = minimum_cut_phase();
522 best_cut = std::min(best_cut, cut_weight);
523 merge_nodes(s, t);
524 }
525
526 return best_cut;
527 }
528 };
529
541 template <class GT>
543 {
545 size_t operator()(typename GT::Arc*) const { return 1; }
546 };
547
548} // namespace Aleph
549
550# endif // STOER_WAGNER_H
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
long double w
Definition btreepic.C:153
Default distance accessor for arc weights.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & top() const
Definition htlist.H:1683
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
T & push(const T &item)
Definition htlist.H:1523
void empty() noexcept
empty the list
Definition htlist.H:1689
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
Graph_Arc< int > Arc
The node class type.
Definition tpl_graph.H:433
Stoer-Wagner deterministic minimum cut algorithm.
std::vector< std::vector< Weight > > adj
Weight operator()(GT &g, DynList< Node * > &vs, DynList< Node * > &vt, DynList< Arc * > &cut)
Find minimum cut in graph.
void merge_nodes(size_t s, size_t t)
std::tuple< size_t, size_t, Weight > minimum_cut_phase()
std::vector< SWNode > nodes
Stoer_Wagner_Min_Cut()=default
Default constructor.
Stoer_Wagner_Min_Cut(Distance _distance)
Constructor with custom distance functor.
typename Distance::Distance_Type Weight
Weight min_cut_weight(GT &g)
Find only the minimum cut weight (faster, no partition info).
bool exists(Operation &op) const
Test for existence in the container of an element satisfying a criteria.
Definition ah-dry.H:846
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:731
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:737
List_Graph< Graph_Node< int >, Graph_Arc< int > > GT
#define NODE_COUNTER(p)
Get the counter of a node.
Singly linked list implementations with head-tail access.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
void next()
Advance all underlying iterators (bounds-checked).
Definition ah-zip.H:175
DynList< T > maps(const C &c, Op op)
Classic map operation.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
bool operator<(const PQEntry &other) const
Unit weight functor for unweighted graphs.
size_t operator()(typename GT::Arc *) const
Distance accessor.
Lazy and scalable dynamic array implementation.
Utility algorithms and operations for graphs.
Simple graph implementation with adjacency lists.