Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_bipartite.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
48#ifndef TPL_BIPARTITE_H
49#define TPL_BIPARTITE_H
50
51# include <ah-graph-concepts.H>
52
53#include <tpl_dynDlist.H>
54#include <tpl_dynMapTree.H>
55#include <tpl_dynListQueue.H>
56#include <tpl_net.H>
57#include <ah-errors.H>
58#include <cookie_guard.H>
59#include <limits>
60
61namespace Aleph {
68
69template <AlephGraph GT>
70static long &color(typename GT::Node *p)
71{
72 return NODE_COUNTER(p);
73}
74
75template <AlephGraph GT>
76static long &color(typename GT::Arc *a)
77{
78 return ARC_COUNTER(a);
79}
80
98template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
100{
101 g.reset_nodes(); // initialize counters to White
102 g.reset_arcs();
103
104 if (g.get_num_nodes() == 0)
105 return;
106
108 typename GT::Node *p = g.get_first_node();
109 color<GT>(p) = Bp_Red;
110 red.put(p);
111 l.put(p);
112
113 while (true)
114 if (not red.is_empty()) // any red node with unvisited arcs?
115 {
116 typename GT::Node *p = red.get();
117 for (Node_Arc_Iterator<GT, SA> it(p); it.has_curr(); it.next_ne())
118 {
119 typename GT::Arc *a = it.get_curr();
120 ah_domain_error_if(color<GT>(a) == Bp_Red) << "Graph is not bipartite";
121 if (color<GT>(a) == Bp_Blue)
122 continue;
123
124 color<GT>(a) = Bp_Red;
125 typename GT::Node *q = it.get_tgt_node();
126 ah_domain_error_if(color<GT>(q) == Bp_Red) << "Graph is not bipartite";
127 if (color<GT>(q) == Bp_Blue)
128 continue;
129
130 color<GT>(q) = Bp_Blue;
131 blue.put(q);
132 r.put(q);
133 }
134 }
135 else if (not blue.is_empty()) // any blue node with unvisited arcs?
136 {
137 typename GT::Node *p = blue.get();
138 for (Node_Arc_Iterator<GT, SA> it(p); it.has_curr(); it.next_ne())
139 {
140 typename GT::Arc *a = it.get_curr();
141 ah_domain_error_if(color<GT>(a) == Bp_Blue) << "Graph is not bipartite";
142 if (color<GT>(a) == Bp_Red)
143 continue;
144
145 color<GT>(a) = Bp_Blue;
146
147 typename GT::Node *q = it.get_tgt_node();
148 ah_domain_error_if(color<GT>(q) == Bp_Blue) << "Graph is not bipartite";
149 if (color<GT>(q) == Bp_Red)
150 continue;
151
152 color<GT>(q) = Bp_Red;
153 red.put(q);
154 l.put(q);
155 }
156 }
157 else
158 break;
159}
160
169template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
187
209template <AlephGraph GT, template <class> class Max_Flow = Ford_Fulkerson_Maximum_Flow, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
212{
213 if (g.get_num_nodes() == 0)
214 return;
215
217
218 compute_bipartite(g, l, r);
219
221 AN net;
222
223 // Cookie_Guard ensures cookies are cleared when a function exits
224 // (prevents dangling pointers to destroyed 'net' nodes)
225 Cookie_Guard<GT> cookie_guard(g, true, true);
226
227 // Use a map to store arc mapping since Max_Flow algorithms reset cookies
229
230 // traverse nodes of g and insert their image in the net
231 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne())
232 {
233 typename GT::Node *p = it.get_curr();
234 NODE_COOKIE(p) = net.insert_node();
235 NODE_COOKIE((typename GT::Node *) NODE_COOKIE(p)) = p;
236 }
237
238 typename AN::Node *source = net.insert_node();
239
240 // traverse nodes of l, connect them to the source and insert their arcs
241 for (typename DynDlist<typename GT::Node *>::Iterator i(l); i.has_curr(); i.next_ne())
242 {
243 typename GT::Node *p = i.get_curr();
244 typename AN::Node *src = mapped_node<GT, AN>(p);
245 net.insert_arc(source, src, 1);
246
247 for (Node_Arc_Iterator<GT, SA> j(p); j.has_curr(); j.next_ne())
248 {
249 typename GT::Arc *arc = j.get_current_arc_ne();
250 typename AN::Node *tgt = mapped_node<GT, AN>(g.get_tgt_node(arc));
251 typename AN::Arc *a = net.insert_arc(src, tgt, 1);
252 // Store mapping in map (not cookies, which are cleared by Max_Flow)
253 arc_map.insert(a, arc);
254 }
255 }
256
257 typename AN::Node *sink = net.insert_node();
258
259 // traverse nodes of r and connect them to the sink
260 for (typename DynDlist<typename GT::Node *>::Iterator it(r); it.has_curr(); it.next_ne())
261 {
262 typename GT::Node *p = it.get_curr();
263 net.insert_arc(mapped_node<GT, AN>(p), sink, 1);
264 }
265
266 Max_Flow<AN>()(net);
267
268 for (Arc_Iterator<AN> it(net); it.has_curr(); it.next_ne())
269 {
270 typename AN::Arc *a = it.get_curr();
271 if (a->flow == 0)
272 continue;
273
274 // Look up the original arc in our map
275 auto *pair_ptr = arc_map.search(a);
276 if (pair_ptr == nullptr)
277 continue;
278
279 matching.append(pair_ptr->second);
280 }
281}
282
293template <AlephGraph GT, template <class> class Max_Flow = Ford_Fulkerson_Maximum_Flow, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
316
329template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
333{
334 using Node = typename GT::Node;
335
336 g.reset_nodes();
337 g.reset_arcs();
338
339 for (Node_Iterator<GT> nit(g); nit.has_curr(); nit.next_ne())
340 {
341 Node *start = nit.get_curr();
342 if (color<GT>(start) != Bp_White)
343 continue;
344
345 color<GT>(start) = Bp_Red;
346 l.append(start);
347
349 queue.put(start);
350
351 while (not queue.is_empty())
352 {
353 Node *p = queue.get();
354 const long p_color = color<GT>(p);
355 const long neighbor_color = (p_color == Bp_Red) ? Bp_Blue : Bp_Red;
356
357 for (Node_Arc_Iterator<GT, SA> it(p); it.has_curr(); it.next_ne())
358 if (Node *q = it.get_tgt_node(); color<GT>(q) == Bp_White)
359 {
361 if (neighbor_color == Bp_Red)
362 l.append(q);
363 else
364 r.append(q);
365 queue.put(q);
366 }
367 else if (color<GT>(q) == p_color)
368 return false;
369 }
370 }
371
372 return true;
373}
374
375// Helper: DFS for augmenting path in Hopcroft-Karp.
376// Returns true if an augmenting path from u to a free right-node
377// was found and the matching was augmented along it.
378template <AlephGraph GT, ArcFilter<GT> SA>
379static bool hopcroft_karp_dfs(const GT &g, typename GT::Node *u)
380{
381 using Node = typename GT::Node;
382 using Arc = typename GT::Arc;
383
384 constexpr long INF = std::numeric_limits<long>::max();
385 long &u_dist = NODE_COUNTER(u);
386
387 for (Node_Arc_Iterator<GT, SA> it(u); it.has_curr(); it.next_ne())
388 {
389 Arc *arc = it.get_current_arc_ne();
390 Node *v = g.get_connected_node(arc, u); // right-side node
391
392 // v's match partner (maybe nullptr if v is free)
393 Arc *match_arc = static_cast<Arc *>(NODE_COOKIE(v));
394 Node *pair_v = (match_arc != nullptr) ? g.get_connected_node(match_arc, v) : nullptr;
395
396 if (pair_v == nullptr) // v is free → augmenting path found
397 {
398 NODE_COOKIE(u) = arc;
399 NODE_COOKIE(v) = arc;
400 u_dist = INF;
401 return true;
402 }
403
404 // pair_v is a left-side node matched to v
405 if (NODE_COUNTER(pair_v) == u_dist + 1)
407 {
408 NODE_COOKIE(u) = arc;
409 NODE_COOKIE(v) = arc;
410 u_dist = INF;
411 return true;
412 }
413 }
414
415 u_dist = INF; // mark u as "dead" for this phase
416 return false;
417}
418
433template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
435{
436 using Node = typename GT::Node;
437 using Arc = typename GT::Arc;
438
439 constexpr long INF = std::numeric_limits<long>::max();
440
441 if (g.get_num_nodes() == 0)
442 return;
443
445
446 // 2-coloring (uses NODE_COUNTER for color)
448 ah_domain_error_if(not is_bipartite) << "Graph is not bipartite";
449
450 // Now repurpose NODE_COOKIE for matching arcs and
451 // NODE_COUNTER for BFS distances (left-side only).
452 // Cookie_Guard clears cookies on exit.
453 Cookie_Guard<GT> cookie_guard(g, true, false);
454
455 // Initialize: all nodes are free (NODE_COOKIE = nullptr already from guard)
456 // Set all left-side distances to INF
457 for (auto it = left_nodes.get_it(); it.has_curr(); it.next_ne())
458 NODE_COUNTER(it.get_curr()) = INF;
459 for (auto it = right_nodes.get_it(); it.has_curr(); it.next_ne())
460 NODE_COUNTER(it.get_curr()) = 0; // right-side counters unused
461
462 // BFS phase: returns true if augmenting paths exist
463 // Sets distances on left-side nodes
464 auto bfs = [&]() -> bool
465 {
467 long dist_to_free = INF;
468
469 // Enqueue all free left-side nodes at distance 0
470 for (auto it = left_nodes.get_it(); it.has_curr(); it.next_ne())
471 if (Node *u = it.get_curr(); NODE_COOKIE(u) == nullptr) // u is free
472 {
473 NODE_COUNTER(u) = 0;
474 queue.put(u);
475 }
476 else
477 NODE_COUNTER(u) = INF;
478
479 while (not queue.is_empty())
480 {
481 Node *u = queue.get();
482 const long u_dist = NODE_COUNTER(u);
483
484 if (u_dist >= dist_to_free)
485 continue;
486
487 for (Node_Arc_Iterator<GT, SA> it(u); it.has_curr(); it.next_ne())
488 {
489 Arc *arc = it.get_current_arc_ne();
490 Node *v = g.get_connected_node(arc, u); // right-side
491
492 Arc *match_arc = static_cast<Arc *>(NODE_COOKIE(v));
493 Node *pair_v = (match_arc != nullptr) ? g.get_connected_node(match_arc, v) : nullptr;
494
495 if (pair_v == nullptr) // v is free
496 dist_to_free = u_dist + 1;
497 else if (NODE_COUNTER(pair_v) == INF)
498 {
500 queue.put(pair_v);
501 }
502 }
503 }
504
505 return dist_to_free != INF;
506 };
507
508 // Main loop: BFS + DFS phases
509 while (bfs())
510 {
511 for (auto it = left_nodes.get_it(); it.has_curr(); it.next_ne())
512 if (Node *u = it.get_curr(); NODE_COOKIE(u) == nullptr) // u is free
514 }
515
516 // Collect matching arcs from left-side nodes
518 for (auto it = left_nodes.get_it(); it.has_curr(); it.next_ne())
519 {
520 Arc *a = static_cast<Arc *>(NODE_COOKIE(it.get_curr()));
521 if (a != nullptr and not seen.contains(a))
522 {
523 matching.append(a);
524 seen.insert(a);
525 }
526 }
527}
528
535template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
550} // end namespace Aleph
551
552#endif // TPL_BIPARTITE_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.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
Class that takes a bipartite graph and computes the partition sets.
void operator()(const GT &g, DynDlist< typename GT::Node * > &l, DynDlist< typename GT::Node * > &r)
Computes the partition sets of a bipartite graph.
Class for computing the maximum cardinality matching of a bipartite graph.
void operator()(const GT &g, DynDlist< typename GT::Arc * > &matching)
Computes the maximum bipartite matching of a graph.
RAII guard that clears graph cookies on destruction.
Iterator dynamic list.
Dynamic doubly linked list with O(1) size and bidirectional access.
Dynamic queue of elements of generic type T based on single linked list.
T & put(const T &data)
The type of element.
T get()
Remove the oldest item of the queue.
bool is_empty() const noexcept
Return true if this is empty.
T & append(const T &item)
Definition htlist.H:1271
T & put(const T &item)
Definition htlist.H:1250
Generic key-value map implemented on top of a binary search tree.
Dynamic set implemented using AVL binary search trees of type Avl_Tree<Key>.
Key * append(const Key &key)
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Functor wrapper for hopcroft_karp_matching.
void operator()(const GT &g, DynDlist< typename GT::Arc * > &matching)
Computes the maximum matching using Hopcroft-Karp.
Node * get_first_node() const
Return any node in the graph.
Definition tpl_graph.H:577
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1207
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
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:737
Node * get_connected_node(Arc *arc, Node *node) const noexcept
Return the adjacent node to node through arc.
Definition graph-dry.H:820
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
RAII guards for graph node/arc cookies.
void compute_bipartite(const GT &g, DynDlist< typename GT::Node * > &l, DynDlist< typename GT::Node * > &r)
Computes the partition sets of a bipartite graph.
bool compute_bipartite_all_components(const GT &g, DynDlist< typename GT::Node * > &l, DynDlist< typename GT::Node * > &r)
Computes the partition sets of a bipartite graph handling all connected components.
#define NODE_COUNTER(p)
Get the counter of a node.
void compute_maximum_cardinality_bipartite_matching(const GT &g, DynDlist< typename GT::Arc * > &matching)
Computes the maximum cardinality matching of a bipartite graph.
#define ARC_COUNTER(p)
Return the counter of arc p.
#define NODE_COOKIE(p)
Return the node cookie
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
void hopcroft_karp_matching(const GT &g, DynDlist< typename GT::Arc * > &matching)
Computes a maximum cardinality matching on a bipartite graph using the Hopcroft-Karp algorithm.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
static bool hopcroft_karp_dfs(const GT &g, typename GT::Node *u)
and
Check uniqueness with explicit hash + equality functors.
static long & color(typename GT::Node *p)
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
Functor wrapper for ford_fulkerson_maximum_flow().
Definition tpl_net.H:1534
Arc of a flow network implemented with adjacency lists.
Definition tpl_net.H:115
Flow network implemented with adjacency lists.
Definition tpl_net.H:261
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1120
gsl_rng * r
Dynamic doubly linked list implementation.
Dynamic queue implementation based on linked lists.
Dynamic key-value map based on balanced binary search trees.
Network flow graph structures.
DynList< int > l