Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Karger.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
43# ifndef KARGER_H
44# define KARGER_H
45
46# include <cmath>
47# include <limits>
48# include <vector>
49# include <htlist.H>
50# include <tpl_sgraph.H>
51# include <generate_graph.H>
52# include <tpl_graph_utils.H>
53# include <ah-errors.H>
54
55namespace Aleph
56{
156 template <class GT, class SA = Dft_Show_Arc<GT>>
158 {
159 // Each node contains the list of contracted nodes
163
164 using Node = typename GT::Node;
165 using Arc = typename GT::Arc;
166
171 {
172 std::vector<Karc *> arcs;
173
174 public:
175 void insert(Karc *a)
176 {
177 ARC_COUNTER(a) = static_cast<long>(arcs.size());
178 arcs.push_back(a);
179 }
180
181 Karc * select(size_t i) const
182 {
183 assert(i < arcs.size());
184 return arcs[i];
185 }
186
187 void remove(Karc *a)
188 {
189 const long raw_idx = ARC_COUNTER(a);
190 if (raw_idx < 0) // Already removed (parallel arc processed twice)
191 return;
192
193 auto idx = static_cast<size_t>(raw_idx);
194 assert(idx < arcs.size());
195 assert(arcs[idx] == a);
196 if (idx < arcs.size() - 1)
197 {
198 arcs[idx] = arcs.back();
199 ARC_COUNTER(arcs[idx]) = static_cast<long>(idx);
200 }
201 arcs.pop_back();
202 ARC_COUNTER(a) = -1; // Mark as removed
203 }
204
205 void empty() { arcs.clear(); }
206
207 [[nodiscard]] size_t size() const { return arcs.size(); }
208
209 void swap(ArcIndex & other) noexcept { arcs.swap(other.arcs); }
210 };
211
212
213 bool has_self_loop(const GT & g) const
214 {
215 for (typename GT::Arc_Iterator it(g); it.has_curr(); it.next_ne())
216 if (auto a = it.get_curr(); g.get_src_node(a) == g.get_tgt_node(a))
217 return true;
218 return false;
219 }
220
221 bool is_connected_filtered(const GT & g) const
222 {
223 if (g.get_num_nodes() == 0)
224 return false;
225
227 return dft(g) == g.get_num_nodes();
228 }
229
230 void validate_graph(GT & g) const
231 {
232 ah_domain_error_if(g.get_num_nodes() < 2) << "Graph must have at least 2 nodes";
233 ah_domain_error_if(g.get_num_arcs() == 0) << "Graph has no arcs";
234 ah_domain_error_if(has_self_loop(g)) << "Graph contains self-loop";
235 ah_domain_error_if(not is_connected_filtered(g)) << "Graph is disconnected";
236 }
237
238 unsigned long seed;
241
248 {
250 arcs.empty();
251 g.reset_nodes();
252 g.reset_arcs();
253
254 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
255 {
256 auto p = it.get_curr();
257 auto q = kg.insert_node();
258 q->get_info().append(p);
259 g.map_nodes(p, q);
260 }
261
262 // Use arc filter SA to determine which arcs to include
263 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
264 {
265 auto a = it.get_curr();
268 auto ka = kg.insert_arc(s, t, a);
269 arcs.insert(ka);
270 }
271 }
272
282 ArcIndex & arcs)
283 {
284 for (typename Kgraph::Node_Arc_Iterator it(p); it.has_curr(); it.next_ne())
285 {
286 auto pa = it.get_curr();
287 auto tgt = it.get_tgt_node_ne();
288
289 arcs.remove(pa); // remove from index; removed from graph when nodes deleted
290 if (tgt == t)
291 continue; // parallel arc ==> ignore
292
293 auto ka = kg.insert_arc(cp, tgt, pa->get_info());
294 arcs.insert(ka);
295 }
296 }
297
301 {
302 arcs.empty();
303 for (typename Kgraph::Arc_Iterator it(kg); it.has_curr(); it.next_ne())
304 arcs.insert(it.get_curr());
305 }
306
316 ArcIndex & arcs)
317 {
318 while (kg.get_num_nodes() > left_num_nodes)
319 { // randomly select an arc from kg
320 assert(arcs.size() == kg.get_num_arcs() &&
321 "Arc index and graph arc count must be in sync");
322 auto num_arc = gsl_rng_uniform_int(r, arcs.size());
323 auto a = arcs.select(num_arc); // arc to remove
324 auto s = kg.get_src_node(a); // nodes to "contract"
325 auto t = kg.get_tgt_node(a);
326
327 arcs.remove(a); // remove from kg and index
328 kg.remove_arc(a);
329
330 auto cp = kg.insert_node(); // new contracted node representing s-t
331
332 update_arcs(kg, s, t, cp, arcs);
333 update_arcs(kg, t, s, cp, arcs);
334
335 cp->get_info().swap(s->get_info());
336 cp->get_info().append(t->get_info());
337
338 kg.remove_node(s);
339 kg.remove_node(t);
340 }
341 }
342
351 size_t karger_min_cut(GT & g,
355 const size_t num_iter)
356 {
358
359 auto min_cut = std::numeric_limits<size_t>::max();
360
361 Kgraph kg;
362 ArcIndex arcs; // reused across iterations
363
364 for (size_t i = 0; i < num_iter; ++i)
365 {
366 build_kgraph(g, kg, arcs);
367
368 contract(kg, 2, arcs);
369
370 const size_t cut_size = kg.get_num_arcs();
371
372 if (cut_size >= min_cut)
373 continue;
374
376
377 // Early termination: cut of 1 is optimal for connected graphs
378 if (min_cut == 1)
379 {
380 auto ka = kg.get_first_arc();
381 cut.empty();
382 cut.append(ka->get_info());
383 vs.empty();
384 vt.empty();
385 vs.swap(kg.get_src_node(ka)->get_info());
386 vt.swap(kg.get_tgt_node(ka)->get_info());
387 return 1;
388 }
389
390 // update minimum cut
391 cut.empty();
392
393 // traverse the arcs of the super nodes (these are the cut edges)
394 for (typename Kgraph::Arc_Iterator it(kg); it.has_curr(); it.next_ne())
395 {
396 auto ka = it.get_curr();
397 assert(kg.get_src_node(ka) != kg.get_tgt_node(ka));
398
399 cut.append(ka->get_info());
400 }
401
402 auto ka = kg.get_first_arc();
403 auto S = kg.get_src_node(ka);
404 auto T = kg.get_tgt_node(ka);
405
406 assert(S->get_info().size() + T->get_info().size() ==
407 g.get_num_nodes());
408
409 vs.empty();
410 vt.empty();
411 vs.swap(S->get_info());
412 vt.swap(T->get_info());
413 }
414 return min_cut;
415 }
416
420 {
421 const size_t n = kg.get_num_nodes();
422
423 if (n <= 6)
424 {
425 // Base case: for small graphs, contract to 2 nodes and return cut
426 contract(kg, 2, arcs);
427 return kg.get_num_arcs();
428 }
429
430 // Contract to t = ceil(1 + n/sqrt(2)) nodes
431 const auto t = static_cast<size_t>(std::ceil(1.0 + n / std::sqrt(2.0)));
432
433 // Branch 1: copy graph and rebuild arc index for the copy
434 Kgraph h1(kg);
437 contract(h1, t, arcs1);
438 const size_t cut1 = __fast_karger_min_cut(h1, arcs1);
439
440 // Branch 2: copy graph and rebuild arc index for the copy
441 Kgraph h2(kg);
444 contract(h2, t, arcs2);
445 const size_t cut2 = __fast_karger_min_cut(h2, arcs2);
446
447 // Return the better cut
448 if (cut1 < cut2)
449 {
450 kg.swap(h1);
451 arcs.swap(arcs1);
452 return cut1;
453 }
454
455 kg.swap(h2);
456 arcs.swap(arcs2);
457 return cut2;
458 }
459
460 public:
469 Karger_Min_Cut(const unsigned long _seed = time(nullptr), SA _sa = SA())
471 {
473 }
474
477 {
478 if (r)
480 }
481
484
487
490 : seed(other.seed), r(other.r), sa(std::move(other.sa))
491 {
492 other.r = nullptr;
493 }
494
497 {
498 if (this != &other)
499 {
500 if (r)
502 seed = other.seed;
503 r = other.r;
504 sa = std::move(other.sa);
505 other.r = nullptr;
506 }
507 return *this;
508 }
509
511 [[nodiscard]] unsigned long get_seed() const noexcept { return seed; }
512
515 void reseed(const unsigned long new_seed) noexcept
516 {
517 seed = new_seed;
518 if (r)
520 }
521
532 size_t compute_min_cut_size(GT & g, size_t num_iter = 0)
533 {
534 assert(r != nullptr && "Cannot use moved-from Karger_Min_Cut object");
536
537 if (num_iter == 0)
538 {
539 const size_t n = g.get_num_nodes();
540 num_iter = static_cast<size_t>(1.05 * n * n);
541 }
542
543 size_t min_cut = std::numeric_limits<size_t>::max();
544
545 Kgraph kg;
547
548 for (size_t i = 0; i < num_iter; ++i)
549 {
550 build_kgraph(g, kg, arcs);
551 contract(kg, 2, arcs);
552
553 const size_t cut_size = kg.get_num_arcs();
554 if (cut_size < min_cut)
555 {
557 if (min_cut == 1)
558 return 1; // Early termination
559 }
560 }
561 return min_cut;
562 }
563
574 size_t operator ()(GT & g,
578 const size_t num_iter)
579 {
580 assert(r != nullptr && "Cannot use moved-from Karger_Min_Cut object");
581 return karger_min_cut(g, vs, vt, cut, num_iter);
582 }
583
596 size_t operator ()(GT & g,
600 {
601 assert(r != nullptr && "Cannot use moved-from Karger_Min_Cut object");
602 const size_t n = g.get_num_nodes();
603 // 1.05 * n^2 iterations gives high probability of finding min cut
604 const auto num_iter = static_cast<size_t>(1.05 * n * n);
605 return karger_min_cut(g, vs, vt, cut, num_iter);
606 }
607
624 size_t fast(GT & g,
628 size_t num_iter = 0)
629 {
630 assert(r != nullptr && "Cannot use moved-from Karger_Min_Cut object");
632
633 // Default iterations: ceil(log^2 n) for high probability
634 if (num_iter == 0)
635 {
636 const double log_n = std::log2(static_cast<double>(g.get_num_nodes()));
637 num_iter = static_cast<size_t>(std::ceil(log_n * log_n));
638 if (num_iter < 1)
639 num_iter = 1;
640 }
641
642 size_t best_cut = std::numeric_limits<size_t>::max();
643
644 for (size_t i = 0; i < num_iter; ++i)
645 {
646 Kgraph kg;
648 build_kgraph(g, kg, arcs);
649
650 const size_t current_cut = __fast_karger_min_cut(kg, arcs);
651
652 if (current_cut >= best_cut)
653 continue;
654
656
657 cut.empty();
658 for (typename Kgraph::Arc_Iterator it(kg); it.has_curr(); it.next_ne())
659 cut.append(it.get_curr()->get_info());
660
661 auto ka = kg.get_first_arc();
662 auto S = kg.get_src_node(ka);
663 auto T = kg.get_tgt_node(ka);
664
665 vs.empty();
666 vt.empty();
667 vs.swap(S->get_info());
668 vt.swap(T->get_info());
669
670 // Early termination
671 if (best_cut == 1)
672 return 1;
673 }
674
675 return best_cut;
676 }
677 };
678} // namespace Aleph
679
680# endif // KARGER_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
Stateful depth-first traversal functor.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
T remove()
Remove the first item of the list.
Definition htlist.H:1611
void empty() noexcept
empty the list
Definition htlist.H:1689
DynList & swap(DynList &l) noexcept
Swap this with l.
Definition htlist.H:1448
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Arc index with O(1) operations and deterministic ordering.
Definition Karger.H:171
Karc * select(size_t i) const
Definition Karger.H:181
std::vector< Karc * > arcs
Definition Karger.H:172
void swap(ArcIndex &other) noexcept
Definition Karger.H:209
Karger's randomized minimum cut algorithm.
Definition Karger.H:158
size_t compute_min_cut_size(GT &g, size_t num_iter=0)
Compute only the minimum cut size (without partition info).
Definition Karger.H:532
unsigned long get_seed() const noexcept
Get the random seed used by this solver (useful for reproducibility).
Definition Karger.H:511
Karger_Min_Cut(Karger_Min_Cut &&other) noexcept
Move constructor. Transfers ownership of the random number generator.
Definition Karger.H:489
typename GT::Node Node
Definition Karger.H:164
Karger_Min_Cut(const Karger_Min_Cut &)=delete
Disable copy constructor (class owns gsl_rng pointer)
size_t operator()(GT &g, DynList< typename GT::Node * > &vs, DynList< typename GT::Node * > &vt, DynList< typename GT::Arc * > &cut, const size_t num_iter)
Compute a minimum cut with a specified number of iterations.
Definition Karger.H:574
void contract(Kgraph &kg, size_t left_num_nodes, ArcIndex &arcs)
Contract the graph by randomly merging edges until left_num_nodes remain.
Definition Karger.H:315
bool has_self_loop(const GT &g) const
Definition Karger.H:213
size_t __fast_karger_min_cut(Kgraph &kg, ArcIndex &arcs)
Karger-Stein fast minimum cut algorithm (recursive version).
Definition Karger.H:419
Karger_Min_Cut & operator=(Karger_Min_Cut &&other) noexcept
Move assignment operator. Transfers ownership of the random number generator.
Definition Karger.H:496
void rebuild_arc_index(Kgraph &kg, ArcIndex &arcs)
Rebuild the arc index from scratch for the given graph.
Definition Karger.H:300
~Karger_Min_Cut()
Destructor. Frees the random number generator (if not moved).
Definition Karger.H:476
bool is_connected_filtered(const GT &g) const
Definition Karger.H:221
void update_arcs(Kgraph &kg, Knode *p, Knode *t, Knode *cp, ArcIndex &arcs)
Update arcs when contracting nodes p and t into cp.
Definition Karger.H:281
Karger_Min_Cut(const unsigned long _seed=time(nullptr), SA _sa=SA())
Construct a Karger minimum cut solver.
Definition Karger.H:469
size_t fast(GT &g, DynList< typename GT::Node * > &vs, DynList< typename GT::Node * > &vt, DynList< typename GT::Arc * > &cut, size_t num_iter=0)
Compute minimum cut using Karger-Stein fast algorithm.
Definition Karger.H:624
void validate_graph(GT &g) const
Definition Karger.H:230
Karger_Min_Cut & operator=(const Karger_Min_Cut &)=delete
Disable copy assignment (class owns gsl_rng pointer)
void build_kgraph(GT &g, Kgraph &kg, ArcIndex &arcs)
Build the contracted graph representation from the original graph.
Definition Karger.H:247
typename GT::Arc Arc
Definition Karger.H:165
void reseed(const unsigned long new_seed) noexcept
Change the random seed.
Definition Karger.H:515
unsigned long seed
Definition Karger.H:238
size_t karger_min_cut(GT &g, DynList< typename GT::Node * > &vs, DynList< typename GT::Node * > &vt, DynList< typename GT::Arc * > &cut, const size_t num_iter)
Internal implementation of Karger's minimum cut algorithm.
Definition Karger.H:351
Iterator on the arcs of a graph.
Definition tpl_graph.H:830
Graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:428
Graph_Arc< int > Arc
The node class type.
Definition tpl_graph.H:433
void reset_arcs() const
Reset all the arcs of graph (the control bits, the state, the counter and the cookie)
Definition graph-dry.H:927
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
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:778
void reset_nodes() const
Reset all the nodes of graph (the control bits, the state, the counter and the cookie)
Definition graph-dry.H:920
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:737
static void map_nodes(N1 *p, N2 *q) noexcept
Map the nodes through their cookies.
Definition graph-dry.H:995
Graph visualization and output generation utilities.
DynArray< Graph::Arc * > arcs
Definition graphpic.C:408
#define ARC_COUNTER(p)
Return the counter of arc p.
void clear_graph(GT &g) noexcept
Clean a graph: all its nodes and arcs are removed and freed.
Definition tpl_graph.H:3549
Singly linked list implementations with head-tail access.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:107
Net::Flow_Type min_cut(Net &net, DynSetTree< typename Net::Node * > &vs, DynSetTree< typename Net::Node * > &vt, DynList< typename Net::Arc * > &cuts, DynList< typename Net::Arc * > &cutt)
Compute max flow and the corresponding minimum cut.
Definition tpl_net.H:1864
DynList< T > maps(const C &c, Op op)
Classic map operation.
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Node belonging to a graph implemented with a double linked adjacency list.
Definition tpl_graph.H:121
Iterator on all arcs of a graph.
Definition tpl_graph.H:917
Utility algorithms and operations for graphs.
Simple graph implementation with adjacency lists.