Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Blossom.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
67#ifndef BLOSSOM_H
68#define BLOSSOM_H
69
70# include <ah-graph-concepts.H>
71
72#include <cstdint>
73#include <cstddef>
74#include <cassert>
75#include <functional>
76#include <utility>
77
78#include <cookie_guard.H>
79#include <tpl_array.H>
80#include <tpl_dynListQueue.H>
81#include <tpl_dynMapTree.H>
82#include <tpl_dynDlist.H>
83#include <tpl_graph.H>
84#include <ah-errors.H>
85
86namespace Aleph {
87namespace blossom_detail {
102template <AlephGraph GT, ArcFilter<GT> SA>
104{
105public:
106 using Node = typename GT::Node;
107 using Arc = typename GT::Arc;
108
109private:
110 using Pair_Key = std::pair<size_t, size_t>;
111
112 static constexpr long No_Vertex = -1;
113
114 const GT &g_;
115 SA sa_;
117
121
128
129 static Pair_Key normalized_pair(size_t u, size_t v) noexcept
130 {
131 if (u > v)
132 std::swap(u, v);
133 return std::make_pair(u, v);
134 }
135
138 {
139 nodes_.empty();
140
142 size_t idx = 0;
143 for (Node_Iterator<GT> it(g_); it.has_curr(); it.next_ne())
144 {
145 Node *p = it.get_curr();
146 nodes_.append(p);
147 NODE_COOKIE(p) = reinterpret_cast<void *>(idx + 1);
148 ++idx;
149 }
150 }
151
154 {
155 const size_t n = nodes_.size();
156 adjacency_.empty();
157 adjacency_.reserve(n);
158 for (size_t i = 0; i < n; ++i)
159 adjacency_.append(Array<size_t>());
160
162
163 for (Arc_Iterator<GT, SA> it(g_, sa_); it.has_curr(); it.next_ne())
164 {
165 Arc *a = it.get_curr_ne();
166 Node *src = g_.get_src_node(a);
167 Node *tgt = g_.get_tgt_node(a);
168
169 const auto src_idx_plus_one = reinterpret_cast<uintptr_t>(NODE_COOKIE(src));
170 const auto tgt_idx_plus_one = reinterpret_cast<uintptr_t>(NODE_COOKIE(tgt));
172 continue;
173
174 const size_t u = src_idx_plus_one - 1;
175 const size_t v = tgt_idx_plus_one - 1;
176 if (u == v) // loops never belong to a matching
177 continue;
178
179 if (const Pair_Key key = normalized_pair(u, v); pair_to_arc_.search(key) == nullptr)
180 pair_to_arc_.insert(key, a);
181 }
182
183 for (typename DynMapTree<Pair_Key, Arc *>::Iterator it(pair_to_arc_); it.has_curr(); it.next_ne())
184 {
185 const auto &pair_item = it.get_curr();
186 const size_t u = pair_item.first.first;
187 const size_t v = pair_item.first.second;
188 adjacency_[u].append(v);
189 adjacency_[v].append(u);
190 }
191 }
192
197 [[nodiscard]] size_t lca(size_t a, size_t b) const
198 {
199 Array<char> visited;
200 visited.reserve(nodes_.size());
201 for (size_t i = 0; i < nodes_.size(); ++i)
202 visited.append(0);
203
204 while (true)
205 {
206 a = static_cast<size_t>(base_[a]);
207 visited[a] = 1;
208 if (match_[a] == No_Vertex)
209 break;
210 a = static_cast<size_t>(parent_[static_cast<size_t>(match_[a])]);
211 }
212
213 while (true)
214 {
215 b = static_cast<size_t>(base_[b]);
216 if (visited[b])
217 return b;
218 if (match_[b] == No_Vertex)
219 break;
220 b = static_cast<size_t>(parent_[static_cast<size_t>(match_[b])]);
221 }
222
223 assert(false and "Blossom::lca fallback reached");
224 return b;
225 }
226
228 void mark_path(size_t v, const size_t blossom_base, size_t child)
229 {
230 while (static_cast<size_t>(base_[v]) != blossom_base)
231 {
232 const auto matched_v = static_cast<size_t>(match_[v]);
233 in_blossom[static_cast<size_t>(base_[v])] = 1;
234 in_blossom[static_cast<size_t>(base_[matched_v])] = 1;
235 parent_[v] = static_cast<long>(child);
236 child = matched_v;
237 v = static_cast<size_t>(parent_[matched_v]);
238 }
239 }
240
242 {
244 }
245
252 long find_augmenting_path(const size_t root)
253 {
254 const size_t n = nodes_.size();
255
256 for (size_t i = 0; i < n; ++i)
257 {
258 parent_[i] = No_Vertex;
259 in_queue_[i] = 0;
260 }
261 for (size_t i = 0; i < n; ++i)
262 base_[i] = static_cast<long>(i);
263
264 clear_queue();
266 in_queue_[root] = 1;
267
268 while (not bfs_queue_.is_empty())
269 {
270 const size_t v = bfs_queue_.get();
271 for (const size_t u : adjacency_[v])
272 {
273 if (base_[v] == base_[u] or match_[v] == static_cast<long>(u))
274 continue;
275
276 const bool creates_blossom = u == root or
277 (match_[u] != No_Vertex and parent_[static_cast<size_t>(match_[u])] != No_Vertex);
278
279 if (creates_blossom)
280 {
281 const size_t cur_base = lca(v, u);
282 for (size_t i = 0; i < n; ++i)
283 in_blossom[i] = 0;
284 mark_path(v, cur_base, u);
285 mark_path(u, cur_base, v);
286
287 for (size_t i = 0; i < n; ++i)
288 if (in_blossom[static_cast<size_t>(base_[i])])
289 {
290 base_[i] = static_cast<long>(cur_base);
291 if (not in_queue_[i])
292 {
293 bfs_queue_.put(i);
294 in_queue_[i] = 1;
295 }
296 }
297 }
298 else if (parent_[u] == No_Vertex)
299 {
300 parent_[u] = static_cast<long>(v);
301 if (match_[u] == No_Vertex)
302 return static_cast<long>(u);
303
304 if (const auto m = static_cast<size_t>(match_[u]); not in_queue_[m])
305 {
307 in_queue_[m] = 1;
308 }
309 }
310 }
311 }
312
313 return No_Vertex;
314 }
315
317 void augment_matching(const long endpoint)
318 {
319 long v = endpoint;
320 while (v != No_Vertex)
321 {
322 const long pv = parent_[static_cast<size_t>(v)];
323 if (pv == No_Vertex)
324 break;
325
326 const long next = match_[static_cast<size_t>(pv)];
327 match_[static_cast<size_t>(v)] = pv;
328 match_[static_cast<size_t>(pv)] = v;
329 v = next;
330 }
331 }
332
333public:
335 explicit Edmonds_Blossom_Matcher(const GT &graph, SA __sa = SA())
336 : g_(graph), sa_(std::move(__sa)), cookie_saver_(g_, true, false)
337 {
340
341 const size_t n = nodes_.size();
342 match_.empty();
343 parent_.empty();
344 base_.empty();
347 match_.reserve(n);
348 parent_.reserve(n);
349 base_.reserve(n);
352 for (size_t i = 0; i < n; ++i)
353 {
357 in_queue_.append(0);
359 }
360 }
361
368 size_t solve()
369 {
370 for (long &i : match_)
371 i = No_Vertex;
372
373 for (size_t i = 0; i < nodes_.size(); ++i)
374 {
375 if (match_[i] != No_Vertex)
376 continue;
377
378 if (const long endpoint = find_augmenting_path(i); endpoint != No_Vertex)
380 }
381
382 size_t cardinality = 0;
383 for (size_t i = 0; i < match_.size(); ++i)
384 if (match_[i] != No_Vertex and i < static_cast<size_t>(match_[i]))
385 ++cardinality;
386
387 return cardinality;
388 }
389
392 {
393 return match_;
394 }
395
397 [[nodiscard]] Arc *get_pair_arc(size_t u, size_t v) const noexcept
398 {
399 const Pair_Key key = normalized_pair(u, v);
400 const auto *item = pair_to_arc_.search(key);
401 if (item == nullptr)
402 return nullptr;
403 return item->second;
404 }
405};
406} // namespace blossom_detail
407
432template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
435 SA sa = SA())
436{
438 << "compute_maximum_cardinality_general_matching(): g is a digraph";
439
440 matching.empty();
441
443 const size_t cardinality = matcher.solve();
444
445 const auto &mate = matcher.get_match_vector();
446 for (size_t i = 0; i < mate.size(); ++i)
447 if (mate[i] != -1 and i < static_cast<size_t>(mate[i]))
448 {
449 auto *arc = matcher.get_pair_arc(i, static_cast<size_t>(mate[i]));
450 ah_runtime_error_unless(arc != nullptr)
451 << "Blossom internal error: missing arc for matched pair";
452 matching.append(arc);
453 }
454
455 return cardinality;
456}
457
465template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
472
480template <AlephGraph GT, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
482{
483 SA sa_;
484
485public:
487 {
488 // empty
489 }
490
501};
502} // namespace Aleph
503
504#endif // BLOSSOM_H
Exception handling system with formatted messages for Aleph-w.
#define ah_runtime_error_unless(C)
Throws std::runtime_error if condition does NOT hold.
Definition ah-errors.H:255
#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.
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
void empty() noexcept
Empties the container.
Definition tpl_array.H:341
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Functor wrapper for maximum cardinality general matching.
Definition Blossom.H:482
size_t operator()(const GT &g, DynDlist< typename GT::Arc * > &matching)
Computes a maximum matching.
Definition Blossom.H:497
RAII guard that saves and restores graph cookies.
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.
void clear() noexcept
Empties the container.
bool is_empty() const noexcept
Return true if this is empty.
Generic key-value map implemented on top of a binary search tree.
typename Base::Iterator Iterator
Pair * search(const Key &key) const noexcept
Collect all keys.
Pair * insert(const Key &key, const Data &data)
Insert a key-value pair.
void empty()
remove all elements from the set
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1207
void augment_matching(const long endpoint)
Augment the matching along the path ending at endpoint.
Definition Blossom.H:317
void build_adjacency()
Build a simplified adjacency list for faster traversal.
Definition Blossom.H:153
Arc * get_pair_arc(size_t u, size_t v) const noexcept
Returns the arc connecting node indices u and v.
Definition Blossom.H:397
DynMapTree< Pair_Key, Arc * > pair_to_arc_
Definition Blossom.H:120
void build_node_index()
Index nodes from 0 to n-1 and set up the cookie-based mapping.
Definition Blossom.H:137
size_t solve()
Execute the matching algorithm.
Definition Blossom.H:368
size_t lca(size_t a, size_t b) const
Find the lowest common ancestor of two nodes in the alternating tree.
Definition Blossom.H:197
Edmonds_Blossom_Matcher(const GT &graph, SA __sa=SA())
Initialize the matcher with a graph and an optional filter.
Definition Blossom.H:335
const Array< long > & get_match_vector() const noexcept
Returns the match vector (mate of each node index).
Definition Blossom.H:391
void mark_path(size_t v, const size_t blossom_base, size_t child)
Mark the path from v to the blossom base and set up parents.
Definition Blossom.H:228
long find_augmenting_path(const size_t root)
Search for an augmenting path starting from root.
Definition Blossom.H:252
std::pair< size_t, size_t > Pair_Key
Definition Blossom.H:110
static Pair_Key normalized_pair(size_t u, size_t v) noexcept
Definition Blossom.H:129
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
bool is_digraph() const noexcept
Return true if the graph this is directed.
Definition graph-dry.H:699
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.
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4071
size_t compute_maximum_cardinality_general_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Computes a maximum cardinality matching in a general graph.
Definition Blossom.H:433
#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
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
void next()
Advance all underlying iterators (bounds-checked).
Definition ah-zip.H:171
STL namespace.
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1165
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
Dynamic array container with automatic resizing.
Dynamic doubly linked list implementation.
Dynamic queue implementation based on linked lists.
Dynamic key-value map based on balanced binary search trees.
Generic graph and digraph implementations.