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
31
49# ifndef TPL_BIPARTITE_H
50# define TPL_BIPARTITE_H
51
52# include <tpl_dynDlist.H>
53# include <tpl_dynMapTree.H>
54# include <tpl_dynListQueue.H>
55# include <tpl_net.H>
56# include <ah-errors.H>
57# include <cookie_guard.H>
58# include <limits>
59
60namespace Aleph
61{
63
64 template <class GT>
65 static long &color(typename GT::Node *p)
66 {
67 return NODE_COUNTER(p);
68 }
69
70 template <class GT>
71 static long &color(typename GT::Arc *a)
72 {
73 return ARC_COUNTER(a);
74 }
75
93 template <class GT, class SA = Dft_Show_Arc<GT>>
94 void compute_bipartite(const GT & g,
97 {
98 g.reset_nodes(); // initialize counters to White
99 g.reset_arcs();
100
102 typename GT::Node *p = g.get_first_node();
103 color<GT>(p) = Bp_Red;
104 red.put(p);
105 l.put(p);
106
107 while (true)
108 if (not red.is_empty()) // any red node with unvisited arcs?
109 {
110 typename GT::Node *p = red.get();
111 for (Node_Arc_Iterator<GT, SA> it(p); it.has_curr(); it.next_ne())
112 {
113 typename GT::Arc *a = it.get_curr();
114 ah_domain_error_if(color<GT>(a) == Bp_Red) << "Graph is not bipartite";
115 if (color<GT>(a) == Bp_Blue)
116 continue;
117
118 color<GT>(a) = Bp_Red;
119 typename GT::Node *q = it.get_tgt_node();
120 ah_domain_error_if(color<GT>(q) == Bp_Red) << "Graph is not bipartite";
121 if (color<GT>(q) == Bp_Blue)
122 continue;
123
124 color<GT>(q) = Bp_Blue;
125 blue.put(q);
126 r.put(q);
127 }
128 }
129 else if (not blue.is_empty()) // any blue node with unvisited arcs?
130 {
131 typename GT::Node *p = blue.get();
132 for (Node_Arc_Iterator<GT, SA> it(p); it.has_curr(); it.next_ne())
133 {
134 typename GT::Arc *a = it.get_curr();
135 ah_domain_error_if(color<GT>(a) == Bp_Blue) << "Graph is not bipartite";
136 if (color<GT>(a) == Bp_Red)
137 continue;
138
139 color<GT>(a) = Bp_Blue;
140
141 typename GT::Node *q = it.get_tgt_node();
142 ah_domain_error_if(color<GT>(q) == Bp_Blue) << "Graph is not bipartite";
143 if (color<GT>(q) == Bp_Red)
144 continue;
145
146 color<GT>(q) = Bp_Red;
147 red.put(q);
148 l.put(q);
149 }
150 }
151 else
152 break;
153 }
154
163 template <class GT, class SA = Dft_Show_Arc<GT>>
165 {
166 public:
182 };
183
205 template <class GT,
206 template <class> class Max_Flow = Ford_Fulkerson_Maximum_Flow,
207 class SA = Dft_Show_Arc<GT>>
210 {
212
213 compute_bipartite(g, l, r);
214
216 AN net;
217
218 // Cookie_Guard ensures cookies are cleared when a function exits
219 // (prevents dangling pointers to destroyed 'net' nodes)
220 Cookie_Guard<GT> cookie_guard(g, true, true);
221
222 // Use a map to store arc mapping since Max_Flow algorithms reset cookies
224
225 // traverse nodes of g and insert their image in the net
226 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne())
227 {
228 typename GT::Node *p = it.get_curr();
229 NODE_COOKIE(p) = net.insert_node();
230 NODE_COOKIE((typename GT::Node *) NODE_COOKIE(p)) = p;
231 }
232
233 typename AN::Node *source = net.insert_node();
234
235 // traverse nodes of l, connect them to the source and insert their arcs
237 i.has_curr(); i.next_ne())
238 {
239 typename GT::Node *p = i.get_curr();
240 typename AN::Node *src = mapped_node<GT, AN>(p);
241 net.insert_arc(source, src, 1);
242
243 for (Node_Arc_Iterator<GT, SA> j(p); j.has_curr(); j.next_ne())
244 {
245 typename GT::Arc *arc = j.get_current_arc_ne();
246 typename AN::Node *tgt = mapped_node<GT, AN>(g.get_tgt_node(arc));
247 typename AN::Arc *a = net.insert_arc(src, tgt, 1);
248 // Store mapping in map (not cookies, which are cleared by Max_Flow)
249 arc_map.insert(a, arc);
250 }
251 }
252
253 typename AN::Node *sink = net.insert_node();
254
255 // traverse nodes of r and connect them to the sink
256 for (typename DynDlist<typename GT::Node *>::Iterator it(r);
257 it.has_curr(); it.next_ne())
258 {
259 typename GT::Node *p = it.get_curr();
260 net.insert_arc(mapped_node<GT, AN>(p), sink, 1);
261 }
262
263 Max_Flow<AN>()(net);
264
265 for (Arc_Iterator<AN> it(net); it.has_curr(); it.next_ne())
266 {
267 typename AN::Arc *a = it.get_curr();
268 if (a->flow == 0)
269 continue;
270
271 // Look up the original arc in our map
272 auto *pair_ptr = arc_map.search(a);
273 if (pair_ptr == nullptr)
274 continue;
275
276 matching.append(pair_ptr->second);
277 }
278 }
279
290 template <class GT,
291 template <class> class Max_Flow = Ford_Fulkerson_Maximum_Flow,
292 class SA = Dft_Show_Arc<GT>>
316
329 template <class GT, class 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.
378 template <class GT, class SA>
379 static 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)
406 {
408 {
409 NODE_COOKIE(u) = arc;
410 NODE_COOKIE(v) = arc;
411 u_dist = INF;
412 return true;
413 }
414 }
415 }
416
417 u_dist = INF; // mark u as "dead" for this phase
418 return false;
419 }
420
435 template <class GT, class SA = Dft_Show_Arc<GT>>
438 {
439 using Node = typename GT::Node;
440 using Arc = typename GT::Arc;
441
442 constexpr long INF = std::numeric_limits<long>::max();
443
444 if (g.get_num_nodes() == 0)
445 return;
446
448
449 // 2-coloring (uses NODE_COUNTER for color)
450 const bool is_bipartite =
452 ah_domain_error_if(not is_bipartite) << "Graph is not bipartite";
453
454 // Now repurpose NODE_COOKIE for matching arcs and
455 // NODE_COUNTER for BFS distances (left-side only).
456 // Cookie_Guard clears cookies on exit.
457 Cookie_Guard<GT> cookie_guard(g, true, false);
458
459 // Initialize: all nodes are free (NODE_COOKIE = nullptr already from guard)
460 // Set all left-side distances to INF
461 for (auto it = left_nodes.get_it(); it.has_curr(); it.next_ne())
462 NODE_COUNTER(it.get_curr()) = INF;
463 for (auto it = right_nodes.get_it(); it.has_curr(); it.next_ne())
464 NODE_COUNTER(it.get_curr()) = 0; // right-side counters unused
465
466 // BFS phase: returns true if augmenting paths exist
467 // Sets distances on left-side nodes
468 auto bfs = [&]() -> bool
469 {
471 long dist_to_free = INF;
472
473 // Enqueue all free left-side nodes at distance 0
474 for (auto it = left_nodes.get_it(); it.has_curr(); it.next_ne())
475 if (Node *u = it.get_curr(); NODE_COOKIE(u) == nullptr) // u is free
476 {
477 NODE_COUNTER(u) = 0;
478 queue.put(u);
479 }
480 else
481 NODE_COUNTER(u) = INF;
482
483 while (not queue.is_empty())
484 {
485 Node *u = queue.get();
486 const long u_dist = NODE_COUNTER(u);
487
488 if (u_dist >= dist_to_free)
489 continue;
490
491 for (Node_Arc_Iterator<GT, SA> it(u); it.has_curr(); it.next_ne())
492 {
493 Arc *arc = it.get_current_arc_ne();
494 Node *v = g.get_connected_node(arc, u); // right-side
495
496 Arc *match_arc = static_cast<Arc *>(NODE_COOKIE(v));
497 Node *pair_v = (match_arc != nullptr) ? g.get_connected_node(match_arc, v) : nullptr;
498
499 if (pair_v == nullptr) // v is free
500 dist_to_free = u_dist + 1;
501 else if (NODE_COUNTER(pair_v) == INF)
502 {
504 queue.put(pair_v);
505 }
506 }
507 }
508
509 return dist_to_free != INF;
510 };
511
512 // Main loop: BFS + DFS phases
513 while (bfs())
514 {
515 for (auto it = left_nodes.get_it(); it.has_curr(); it.next_ne())
516 if (Node *u = it.get_curr(); NODE_COOKIE(u) == nullptr) // u is free
518 }
519
520 // Collect matching arcs from left-side nodes
522 for (auto it = left_nodes.get_it(); it.has_curr(); it.next_ne())
523 {
524 Arc *a = static_cast<Arc *>(NODE_COOKIE(it.get_curr()));
525 if (a != nullptr and not seen.contains(a))
526 {
527 matching.append(a);
528 seen.insert(a);
529 }
530 }
531 }
532
539 template <class GT, class SA = Dft_Show_Arc<GT>>
541 {
542 public:
553 };
554} // end namespace Aleph
555
556# 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:522
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.
T & put(const T &item)
T & append(const T &item)
Append a copied item at the end of the list.
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 & 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 & put(const T &item)
Definition htlist.H:1532
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>.
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
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:576
Graph_Arc< int > Arc
The node class type.
Definition tpl_graph.H:433
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1206
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
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
Node * get_connected_node(Arc *arc, Node *node) const noexcept
Return the adjacent node to node through arc.
Definition graph-dry.H:772
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
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
RAII guards for graph node/arc cookies.
List_Graph< Graph_Node< int >, Graph_Arc< int > > GT
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
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)
static long & color(typename GT::Node *p)
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
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Functor wrapper for ford_fulkerson_maximum_flow().
Definition tpl_net.H:1535
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
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:1119
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