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 <ah-graph-concepts.H>
109
110# include <limits>
111# include <vector>
112# include <queue>
113# include <functional>
114# include <htlist.H>
115# include <tpl_sgraph.H>
116# include <tpl_dynArray.H>
117# include <tpl_graph_utils.H>
118# include <ah-errors.H>
119
120namespace Aleph
121{
202 template <AlephGraph GT,
206 {
207 public:
208 using Node = typename GT::Node;
209 using Arc = typename GT::Arc;
211
212 private:
214 SA sa;
215
216 // Internal representation for contraction
217 struct SWNode
218 {
219 DynList<Node*> members; // Original nodes in this super-node
220 size_t id; // Index for adjacency matrix
221 bool merged; // True if merged into another node
222 };
223
224 // Priority queue entry
225 struct PQEntry
226 {
227 size_t node_id;
229
230 bool operator<(const PQEntry& other) const
231 {
232 return priority < other.priority; // Max-heap: higher priority first
233 }
234 };
235
236 // Adjacency matrix for weights (dense representation for efficiency)
237 std::vector<std::vector<Weight>> adj;
238 std::vector<SWNode> nodes;
240
241 // Initialize internal structures from graph
243 {
244 const size_t n = g.get_num_nodes();
245 adj.assign(n, std::vector<Weight>(n, Weight(0)));
246 nodes.resize(n);
247 active_count = n;
248
249 // Assign IDs to nodes
250 size_t id = 0;
251 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
252 {
253 auto p = it.get_curr();
254 NODE_COUNTER(p) = id;
255 nodes[id].members.append(p);
256 nodes[id].id = id;
257 nodes[id].merged = false;
258 ++id;
259 }
260
261 // Build adjacency matrix
262 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
263 {
264 auto a = it.get_curr();
265 size_t src_id = NODE_COUNTER(g.get_src_node(a));
266 size_t tgt_id = NODE_COUNTER(g.get_tgt_node(a));
267 Weight w = distance(a);
268
269 adj[src_id][tgt_id] += w;
270 adj[tgt_id][src_id] += w; // Undirected
271 }
272 }
273
274 // Maximum Adjacency Search: returns (s, t, cut-of-phase weight)
275 std::tuple<size_t, size_t, Weight> minimum_cut_phase()
276 {
277 const size_t n = nodes.size();
278
279 // Find first active node to start
280 size_t start = 0;
281 while (start < n && nodes[start].merged)
282 ++start;
283
284 if (start >= n)
285 return {0, 0, Weight(0)};
286
287 // Priority (sum of weights to nodes in A)
288 std::vector<Weight> key(n, Weight(0));
289 std::vector<bool> in_A(n, false);
290
291 // Use priority queue (max-heap)
292 std::priority_queue<PQEntry> pq;
293
294 // Initialize: add starting node to A
295 in_A[start] = true;
296 size_t last = start;
297 size_t second_last = start;
298
299 // Update priorities for neighbors of start
300 for (size_t j = 0; j < n; ++j)
301 {
302 if (!nodes[j].merged && !in_A[j] && adj[start][j] > Weight(0))
303 {
304 key[j] = adj[start][j];
305 pq.push({j, key[j]});
306 }
307 }
308
309 // Add remaining nodes one by one
310 for (size_t count = 1; count < active_count; ++count)
311 {
312 // Find node with maximum key not in A
313 size_t next = n;
314 while (!pq.empty())
315 {
316 auto top = pq.top();
317 pq.pop();
318
319 if (!in_A[top.node_id] && !nodes[top.node_id].merged &&
320 top.priority == key[top.node_id])
321 {
322 next = top.node_id;
323 break;
324 }
325 }
326
327 if (next >= n)
328 {
329 // Graph is disconnected - find any unvisited node
330 for (size_t j = 0; j < n; ++j)
331 {
332 if (!nodes[j].merged && !in_A[j])
333 {
334 next = j;
335 break;
336 }
337 }
338 }
339
340 if (next >= n)
341 break;
342
343 // Add next to A
344 second_last = last;
345 last = next;
346 in_A[next] = true;
347
348 // Update keys for neighbors of next
349 for (size_t j = 0; j < n; ++j)
350 {
351 if (!nodes[j].merged && !in_A[j] && adj[next][j] > Weight(0))
352 {
353 key[j] += adj[next][j];
354 pq.push({j, key[j]});
355 }
356 }
357 }
358
359 // Cut-of-phase: weight of edges from last to A
360 Weight cut_weight = key[last];
361
362 return {second_last, last, cut_weight};
363 }
364
365 // Merge node t into node s
366 void merge_nodes(size_t s, size_t t)
367 {
368 const size_t n = nodes.size();
369
370 // Move members from t to s
371 nodes[s].members.concat(nodes[t].members);
372 nodes[t].merged = true;
373
374 // Update adjacency: combine edges
375 for (size_t j = 0; j < n; ++j)
376 {
377 if (j != s && j != t && !nodes[j].merged)
378 {
379 adj[s][j] += adj[t][j];
380 adj[j][s] = adj[s][j];
381 }
382 }
383
384 // Clear t's edges
385 for (size_t j = 0; j < n; ++j)
386 {
387 adj[t][j] = Weight(0);
388 adj[j][t] = Weight(0);
389 }
390
391 --active_count;
392 }
393
394 public:
397
404
423 DynList<Node*>& vs,
424 DynList<Node*>& vt,
425 DynList<Arc*>& cut)
426 {
427 const size_t n = g.get_num_nodes();
428 ah_domain_error_if(n < 2) << "Graph must have at least 2 nodes";
429
430 vs.empty();
431 vt.empty();
432 cut.empty();
433
435
436 Weight best_cut = std::numeric_limits<Weight>::max();
437
438 // Main loop: n-1 phases
439 while (active_count > 1)
440 {
441 auto [s, t, cut_weight] = minimum_cut_phase();
442 (void)s; // Suppress unused warning
443
444 if (cut_weight < best_cut)
446
447 // Merge s and t
448 merge_nodes(s, t);
449 }
450
451 // Reconstruct partitions
452 // The best cut separates nodes[best_t].members from the rest
453 // But nodes have been merged, so we need to track this differently
454
455 // Rebuild: run algorithm again but stop at best cut
457
460
461 while (active_count > 1)
462 {
463 auto [s, t, cut_weight] = minimum_cut_phase();
464
465 if (cut_weight == target_cut && cut_side.is_empty())
466 {
467 // This is our target cut - save t's side
468 for (auto p : nodes[t].members)
469 cut_side.append(p);
470 }
471
472 merge_nodes(s, t);
473 }
474
475 // Build partitions
476 for (auto p : cut_side)
477 vt.append(p);
478
479 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
480 {
481 auto p = it.get_curr();
482 if (!cut_side.exists([p](Node* q) { return p == q; }))
483 vs.append(p);
484 }
485
486 // Find cut arcs
487 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
488 {
489 auto a = it.get_curr();
490 auto src = g.get_src_node(a);
491 auto tgt = g.get_tgt_node(a);
492
493 bool src_in_vs = vs.exists([src](Node* p) { return p == src; });
494 bool tgt_in_vs = vs.exists([tgt](Node* p) { return p == tgt; });
495
496 if (src_in_vs != tgt_in_vs)
497 cut.append(a);
498 }
499
500 return best_cut;
501 }
502
512 {
513 const size_t n = g.get_num_nodes();
514 if (n < 2)
515 return Weight(0);
516
518
519 Weight best_cut = std::numeric_limits<Weight>::max();
520
521 while (active_count > 1)
522 {
523 auto [s, t, cut_weight] = minimum_cut_phase();
524 best_cut = std::min(best_cut, cut_weight);
525 merge_nodes(s, t);
526 }
527
528 return best_cut;
529 }
530 };
531
543 template <AlephGraph GT>
545 {
546 using Distance_Type = size_t;
547 size_t operator()(typename GT::Arc*) const { return 1; }
548 };
549
550} // namespace Aleph
551
552# 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:527
C++20 concepts for the protocol shared by graph algorithms.
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
long double w
Definition btreepic.C:153
Default distance accessor for arc weights.
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
void empty() noexcept
empty the list
Definition htlist.H:1389
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
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 criterion.
Definition ah-dry.H:1022
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:779
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:737
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
#define NODE_COUNTER(p)
Get the counter of a node.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
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:171
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:1165
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1001
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.