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 <ah-graph-concepts.H>
47
48# include <cmath>
49# include <limits>
50# include <vector>
51# include <htlist.H>
52# include <tpl_sgraph.H>
53# include <generate_graph.H>
54# include <tpl_graph_utils.H>
55# include <ah-errors.H>
56
57namespace Aleph
58{
158 template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
160 {
161 // Each node contains the list of contracted nodes
165
166 using Node = typename GT::Node;
167 using Arc = typename GT::Arc;
168
173 {
174 std::vector<Karc *> arcs;
175
176 public:
177 void insert(Karc *a)
178 {
179 ARC_COUNTER(a) = static_cast<long>(arcs.size());
180 arcs.push_back(a);
181 }
182
183 Karc * select(size_t i) const
184 {
185 assert(i < arcs.size());
186 return arcs[i];
187 }
188
189 void remove(Karc *a)
190 {
191 const long raw_idx = ARC_COUNTER(a);
192 if (raw_idx < 0) // Already removed (parallel arc processed twice)
193 return;
194
195 auto idx = static_cast<size_t>(raw_idx);
196 assert(idx < arcs.size());
197 assert(arcs[idx] == a);
198 if (idx < arcs.size() - 1)
199 {
200 arcs[idx] = arcs.back();
201 ARC_COUNTER(arcs[idx]) = static_cast<long>(idx);
202 }
203 arcs.pop_back();
204 ARC_COUNTER(a) = -1; // Mark as removed
205 }
206
207 void empty() { arcs.clear(); }
208
209 [[nodiscard]] size_t size() const { return arcs.size(); }
210
211 void swap(ArcIndex & other) noexcept { arcs.swap(other.arcs); }
212 };
213
214
215 bool has_self_loop(const GT & g) const
216 {
217 for (typename GT::Arc_Iterator it(g); it.has_curr(); it.next_ne())
218 if (auto a = it.get_curr(); g.get_src_node(a) == g.get_tgt_node(a))
219 return true;
220 return false;
221 }
222
223 bool is_connected_filtered(const GT & g) const
224 {
225 if (g.get_num_nodes() == 0)
226 return false;
227
229 return dft(g) == g.get_num_nodes();
230 }
231
232 void validate_graph(GT & g) const
233 {
234 ah_domain_error_if(g.get_num_nodes() < 2) << "Graph must have at least 2 nodes";
235 ah_domain_error_if(g.get_num_arcs() == 0) << "Graph has no arcs";
236 ah_domain_error_if(has_self_loop(g)) << "Graph contains self-loop";
237 ah_domain_error_if(not is_connected_filtered(g)) << "Graph is disconnected";
238 }
239
240 unsigned long seed;
242 SA sa;
243
250 {
252 arcs.empty();
253 g.reset_nodes();
254 g.reset_arcs();
255
256 for (typename GT::Node_Iterator it(g); it.has_curr(); it.next_ne())
257 {
258 auto p = it.get_curr();
259 auto q = kg.insert_node();
260 q->get_info().append(p);
261 g.map_nodes(p, q);
262 }
263
264 // Use arc filter SA to determine which arcs to include
265 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
266 {
267 auto a = it.get_curr();
270 auto ka = kg.insert_arc(s, t, a);
271 arcs.insert(ka);
272 }
273 }
274
284 ArcIndex & arcs)
285 {
286 for (typename Kgraph::Node_Arc_Iterator it(p); it.has_curr(); it.next_ne())
287 {
288 auto pa = it.get_curr();
289 auto tgt = it.get_tgt_node_ne();
290
291 arcs.remove(pa); // remove from index; removed from graph when nodes deleted
292 if (tgt == t)
293 continue; // parallel arc ==> ignore
294
295 auto ka = kg.insert_arc(cp, tgt, pa->get_info());
296 arcs.insert(ka);
297 }
298 }
299
303 {
304 arcs.empty();
305 for (typename Kgraph::Arc_Iterator it(kg); it.has_curr(); it.next_ne())
306 arcs.insert(it.get_curr());
307 }
308
318 ArcIndex & arcs)
319 {
320 while (kg.get_num_nodes() > left_num_nodes)
321 { // randomly select an arc from kg
322 assert(arcs.size() == kg.get_num_arcs() &&
323 "Arc index and graph arc count must be in sync");
324 auto num_arc = gsl_rng_uniform_int(r, arcs.size());
325 auto a = arcs.select(num_arc); // arc to remove
326 auto s = kg.get_src_node(a); // nodes to "contract"
327 auto t = kg.get_tgt_node(a);
328
329 arcs.remove(a); // remove from kg and index
330 kg.remove_arc(a);
331
332 auto cp = kg.insert_node(); // new contracted node representing s-t
333
334 update_arcs(kg, s, t, cp, arcs);
335 update_arcs(kg, t, s, cp, arcs);
336
337 cp->get_info().swap(s->get_info());
338 cp->get_info().append(t->get_info());
339
340 kg.remove_node(s);
341 kg.remove_node(t);
342 }
343 }
344
353 size_t karger_min_cut(GT & g,
357 const size_t num_iter)
358 {
360
361 auto min_cut = std::numeric_limits<size_t>::max();
362
363 Kgraph kg;
364 ArcIndex arcs; // reused across iterations
365
366 for (size_t i = 0; i < num_iter; ++i)
367 {
368 build_kgraph(g, kg, arcs);
369
370 contract(kg, 2, arcs);
371
372 const size_t cut_size = kg.get_num_arcs();
373
374 if (cut_size >= min_cut)
375 continue;
376
378
379 // Early termination: cut of 1 is optimal for connected graphs
380 if (min_cut == 1)
381 {
382 auto ka = kg.get_first_arc();
383 cut.empty();
384 cut.append(ka->get_info());
385 vs.empty();
386 vt.empty();
387 vs.swap(kg.get_src_node(ka)->get_info());
388 vt.swap(kg.get_tgt_node(ka)->get_info());
389 return 1;
390 }
391
392 // update minimum cut
393 cut.empty();
394
395 // traverse the arcs of the super nodes (these are the cut edges)
396 for (typename Kgraph::Arc_Iterator it(kg); it.has_curr(); it.next_ne())
397 {
398 auto ka = it.get_curr();
399 assert(kg.get_src_node(ka) != kg.get_tgt_node(ka));
400
401 cut.append(ka->get_info());
402 }
403
404 auto ka = kg.get_first_arc();
405 auto S = kg.get_src_node(ka);
406 auto T = kg.get_tgt_node(ka);
407
408 assert(S->get_info().size() + T->get_info().size() ==
409 g.get_num_nodes());
410
411 vs.empty();
412 vt.empty();
413 vs.swap(S->get_info());
414 vt.swap(T->get_info());
415 }
416 return min_cut;
417 }
418
422 {
423 const size_t n = kg.get_num_nodes();
424
425 if (n <= 6)
426 {
427 // Base case: for small graphs, contract to 2 nodes and return cut
428 contract(kg, 2, arcs);
429 return kg.get_num_arcs();
430 }
431
432 // Contract to t = ceil(1 + n/sqrt(2)) nodes
433 const auto t = static_cast<size_t>(std::ceil(1.0 + n / std::sqrt(2.0)));
434
435 // Branch 1: copy graph and rebuild arc index for the copy
436 Kgraph h1(kg);
439 contract(h1, t, arcs1);
440 const size_t cut1 = __fast_karger_min_cut(h1, arcs1);
441
442 // Branch 2: copy graph and rebuild arc index for the copy
443 Kgraph h2(kg);
446 contract(h2, t, arcs2);
447 const size_t cut2 = __fast_karger_min_cut(h2, arcs2);
448
449 // Return the better cut
450 if (cut1 < cut2)
451 {
452 kg.swap(h1);
453 arcs.swap(arcs1);
454 return cut1;
455 }
456
457 kg.swap(h2);
458 arcs.swap(arcs2);
459 return cut2;
460 }
461
462 public:
471 Karger_Min_Cut(const unsigned long _seed = time(nullptr), SA _sa = SA())
473 {
475 }
476
479 {
480 if (r)
482 }
483
486
489
492 : seed(other.seed), r(other.r), sa(std::move(other.sa))
493 {
494 other.r = nullptr;
495 }
496
499 {
500 if (this != &other)
501 {
502 if (r)
504 seed = other.seed;
505 r = other.r;
506 sa = std::move(other.sa);
507 other.r = nullptr;
508 }
509 return *this;
510 }
511
513 [[nodiscard]] unsigned long get_seed() const noexcept { return seed; }
514
517 void reseed(const unsigned long new_seed) noexcept
518 {
519 seed = new_seed;
520 if (r)
522 }
523
534 size_t compute_min_cut_size(GT & g, size_t num_iter = 0)
535 {
536 assert(r != nullptr && "Cannot use moved-from Karger_Min_Cut object");
538
539 if (num_iter == 0)
540 {
541 const size_t n = g.get_num_nodes();
542 num_iter = static_cast<size_t>(1.05 * n * n);
543 }
544
545 size_t min_cut = std::numeric_limits<size_t>::max();
546
547 Kgraph kg;
549
550 for (size_t i = 0; i < num_iter; ++i)
551 {
552 build_kgraph(g, kg, arcs);
553 contract(kg, 2, arcs);
554
555 const size_t cut_size = kg.get_num_arcs();
556 if (cut_size < min_cut)
557 {
559 if (min_cut == 1)
560 return 1; // Early termination
561 }
562 }
563 return min_cut;
564 }
565
576 size_t operator ()(GT & g,
580 const size_t num_iter)
581 {
582 assert(r != nullptr && "Cannot use moved-from Karger_Min_Cut object");
583 return karger_min_cut(g, vs, vt, cut, num_iter);
584 }
585
598 size_t operator ()(GT & g,
602 {
603 assert(r != nullptr && "Cannot use moved-from Karger_Min_Cut object");
604 const size_t n = g.get_num_nodes();
605 // 1.05 * n^2 iterations gives high probability of finding min cut
606 const auto num_iter = static_cast<size_t>(1.05 * n * n);
607 return karger_min_cut(g, vs, vt, cut, num_iter);
608 }
609
626 size_t fast(GT & g,
630 size_t num_iter = 0)
631 {
632 assert(r != nullptr && "Cannot use moved-from Karger_Min_Cut object");
634
635 // Default iterations: ceil(log^2 n) for high probability
636 if (num_iter == 0)
637 {
638 const double log_n = std::log2(static_cast<double>(g.get_num_nodes()));
639 num_iter = static_cast<size_t>(std::ceil(log_n * log_n));
640 if (num_iter < 1)
641 num_iter = 1;
642 }
643
644 size_t best_cut = std::numeric_limits<size_t>::max();
645
646 for (size_t i = 0; i < num_iter; ++i)
647 {
648 Kgraph kg;
650 build_kgraph(g, kg, arcs);
651
652 const size_t current_cut = __fast_karger_min_cut(kg, arcs);
653
654 if (current_cut >= best_cut)
655 continue;
656
658
659 cut.empty();
660 for (typename Kgraph::Arc_Iterator it(kg); it.has_curr(); it.next_ne())
661 cut.append(it.get_curr()->get_info());
662
663 auto ka = kg.get_first_arc();
664 auto S = kg.get_src_node(ka);
665 auto T = kg.get_tgt_node(ka);
666
667 vs.empty();
668 vt.empty();
669 vs.swap(S->get_info());
670 vt.swap(T->get_info());
671
672 // Early termination
673 if (best_cut == 1)
674 return 1;
675 }
676
677 return best_cut;
678 }
679 };
680} // namespace Aleph
681
682# 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:527
C++20 concepts for the protocol shared by graph algorithms.
Stateful depth-first traversal functor.
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
DynList & swap(DynList &l) noexcept
Definition htlist.H:1176
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:173
Karc * select(size_t i) const
Definition Karger.H:183
std::vector< Karc * > arcs
Definition Karger.H:174
void swap(ArcIndex &other) noexcept
Definition Karger.H:211
Karger's randomized minimum cut algorithm.
Definition Karger.H:160
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:534
unsigned long get_seed() const noexcept
Get the random seed used by this solver (useful for reproducibility).
Definition Karger.H:513
Karger_Min_Cut(Karger_Min_Cut &&other) noexcept
Move constructor. Transfers ownership of the random number generator.
Definition Karger.H:491
typename GT::Node Node
Definition Karger.H:166
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:576
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:317
bool has_self_loop(const GT &g) const
Definition Karger.H:215
size_t __fast_karger_min_cut(Kgraph &kg, ArcIndex &arcs)
Karger-Stein fast minimum cut algorithm (recursive version).
Definition Karger.H:421
Karger_Min_Cut & operator=(Karger_Min_Cut &&other) noexcept
Move assignment operator. Transfers ownership of the random number generator.
Definition Karger.H:498
void rebuild_arc_index(Kgraph &kg, ArcIndex &arcs)
Rebuild the arc index from scratch for the given graph.
Definition Karger.H:302
~Karger_Min_Cut()
Destructor. Frees the random number generator (if not moved).
Definition Karger.H:478
bool is_connected_filtered(const GT &g) const
Definition Karger.H:223
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:283
Karger_Min_Cut(const unsigned long _seed=time(nullptr), SA _sa=SA())
Construct a Karger minimum cut solver.
Definition Karger.H:471
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:626
void validate_graph(GT &g) const
Definition Karger.H:232
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:249
typename GT::Arc Arc
Definition Karger.H:167
void reseed(const unsigned long new_seed) noexcept
Change the random seed.
Definition Karger.H:517
unsigned long seed
Definition Karger.H:240
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:353
Iterator on the arcs of a graph.
Definition tpl_graph.H:831
Graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:429
void reset_arcs() const
Reset all the arcs of graph (the control bits, the state, the counter and the cookie)
Definition graph-dry.H:975
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
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:826
void reset_nodes() const
Reset all the nodes of graph (the control bits, the state, the counter and the cookie)
Definition graph-dry.H:968
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
static void map_nodes(N1 *p, N2 *q) noexcept
Map the nodes through their cookies.
Definition graph-dry.H:1043
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:3659
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
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
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:1863
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1165
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:223
Node belonging to a graph implemented with a double linked adjacency list.
Definition tpl_graph.H:122
Iterator on all arcs of a graph.
Definition tpl_graph.H:918
Utility algorithms and operations for graphs.
Simple graph implementation with adjacency lists.