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
31
63# ifndef BLOSSOM_H
64# define BLOSSOM_H
65
66# include <cstdint>
67# include <cstddef>
68# include <cassert>
69# include <functional>
70# include <utility>
71
72# include <cookie_guard.H>
73# include <tpl_array.H>
74# include <tpl_dynListQueue.H>
75# include <tpl_dynMapTree.H>
76# include <tpl_dynDlist.H>
77# include <tpl_graph.H>
78# include <ah-errors.H>
79
80namespace Aleph
81{
82 namespace blossom_detail
83 {
98 template <class GT, class SA>
100 {
101 public:
102 using Node = typename GT::Node;
103 using Arc = typename GT::Arc;
104
105 private:
106 using Pair_Key = std::pair<size_t, size_t>;
107
108 static constexpr long No_Vertex = -1;
109
110 const GT & g;
113
117
124
125 static Pair_Key normalized_pair(size_t u, size_t v) noexcept
126 {
127 if (u > v)
128 std::swap(u, v);
129 return std::make_pair(u, v);
130 }
131
134 {
135 nodes.empty();
136
138 size_t idx = 0;
139 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne())
140 {
141 Node *p = it.get_curr();
142 nodes.append(p);
143 NODE_COOKIE(p) = reinterpret_cast<void *>(idx + 1);
144 ++idx;
145 }
146 }
147
150 {
151 const size_t n = nodes.size();
152 adjacency.empty();
153 adjacency.reserve(n);
154 for (size_t i = 0; i < n; ++i)
155 adjacency.append(Array<size_t>());
156
158
159 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
160 {
161 Arc *a = it.get_curr_ne();
162 Node *src = g.get_src_node(a);
163 Node *tgt = g.get_tgt_node(a);
164
165 const auto src_idx_plus_one = reinterpret_cast<uintptr_t>(NODE_COOKIE(src));
166 const auto tgt_idx_plus_one = reinterpret_cast<uintptr_t>(NODE_COOKIE(tgt));
168 continue;
169
170 const size_t u = src_idx_plus_one - 1;
171 const size_t v = tgt_idx_plus_one - 1;
172 if (u == v) // loops never belong to a matching
173 continue;
174
175 if (const Pair_Key key = normalized_pair(u, v); pair_to_arc.search(key) == nullptr)
176 pair_to_arc.insert(key, a);
177 }
178
180 it.has_curr();
181 it.next_ne())
182 {
183 const auto & pair_item = it.get_curr();
184 const size_t u = pair_item.first.first;
185 const size_t v = pair_item.first.second;
186 adjacency[u].append(v);
187 adjacency[v].append(u);
188 }
189 }
190
195 [[nodiscard]] size_t lca(size_t a, size_t b) const
196 {
197 Array<char> visited;
198 visited.reserve(nodes.size());
199 for (size_t i = 0; i < nodes.size(); ++i)
200 visited.append(0);
201
202 while (true)
203 {
204 a = static_cast<size_t>(base[a]);
205 visited[a] = 1;
206 if (match[a] == No_Vertex)
207 break;
208 a = static_cast<size_t>(parent[static_cast<size_t>(match[a])]);
209 }
210
211 while (true)
212 {
213 b = static_cast<size_t>(base[b]);
214 if (visited[b])
215 return b;
216 if (match[b] == No_Vertex)
217 break;
218 b = static_cast<size_t>(parent[static_cast<size_t>(match[b])]);
219 }
220
221 assert(false and "Blossom::lca fallback reached");
222 return b;
223 }
224
226 void mark_path(size_t v, const size_t blossom_base, size_t child)
227 {
228 while (static_cast<size_t>(base[v]) != blossom_base)
229 {
230 const auto matched_v = static_cast<size_t>(match[v]);
231 in_blossom[static_cast<size_t>(base[v])] = 1;
232 in_blossom[static_cast<size_t>(base[matched_v])] = 1;
233 parent[v] = static_cast<long>(child);
234 child = matched_v;
235 v = static_cast<size_t>(parent[matched_v]);
236 }
237 }
238
240 {
242 }
243
250 long find_augmenting_path(const size_t root)
251 {
252 const size_t n = nodes.size();
253
254 for (size_t i = 0; i < n; ++i)
255 {
256 parent[i] = No_Vertex;
257 in_queue[i] = 0;
258 }
259 for (size_t i = 0; i < n; ++i)
260 base[i] = static_cast<long>(i);
261
262 clear_queue();
264 in_queue[root] = 1;
265
266 while (not bfs_queue.is_empty())
267 {
268 const size_t v = bfs_queue.get();
269 for (const size_t u: adjacency[v])
270 {
271 if (base[v] == base[u] or match[v] == static_cast<long>(u))
272 continue;
273
274 const bool creates_blossom = u == root or
275 (match[u] != No_Vertex and
276 parent[static_cast<size_t>(match[u])] != No_Vertex);
277
278 if (creates_blossom)
279 {
280 const size_t cur_base = lca(v, u);
281 for (size_t i = 0; i < n; ++i)
282 in_blossom[i] = 0;
283 mark_path(v, cur_base, u);
284 mark_path(u, cur_base, v);
285
286 for (size_t i = 0; i < n; ++i)
287 if (in_blossom[static_cast<size_t>(base[i])])
288 {
289 base[i] = static_cast<long>(cur_base);
290 if (not in_queue[i])
291 {
292 bfs_queue.put(i);
293 in_queue[i] = 1;
294 }
295 }
296 }
297 else if (parent[u] == No_Vertex)
298 {
299 parent[u] = static_cast<long>(v);
300 if (match[u] == No_Vertex)
301 return static_cast<long>(u);
302
303 if (const auto m = static_cast<size_t>(match[u]); not in_queue[m])
304 {
305 bfs_queue.put(m);
306 in_queue[m] = 1;
307 }
308 }
309 }
310 }
311
312 return No_Vertex;
313 }
314
316 void augment_matching(const long endpoint)
317 {
318 long v = endpoint;
319 while (v != No_Vertex)
320 {
321 const long pv = parent[static_cast<size_t>(v)];
322 if (pv == No_Vertex)
323 break;
324
325 const long next = match[static_cast<size_t>(pv)];
326 match[static_cast<size_t>(v)] = pv;
327 match[static_cast<size_t>(pv)] = v;
328 v = next;
329 }
330 }
331
332 public:
334 explicit Edmonds_Blossom_Matcher(const GT & graph, SA __sa = SA())
335 : g(graph), sa(std::move(__sa)), cookie_saver(g, true, false)
336 {
339
340 const size_t n = nodes.size();
341 match.empty();
342 parent.empty();
343 base.empty();
344 in_queue.empty();
346 match.reserve(n);
347 parent.reserve(n);
348 base.reserve(n);
349 in_queue.reserve(n);
351 for (size_t i = 0; i < n; ++i)
352 {
356 in_queue.append(0);
358 }
359 }
360
367 size_t solve()
368 {
369 for (long & i : match)
370 i = No_Vertex;
371
372 for (size_t i = 0; i < nodes.size(); ++i)
373 {
374 if (match[i] != No_Vertex)
375 continue;
376
377 if (const long endpoint = find_augmenting_path(i); endpoint != No_Vertex)
379 }
380
381 size_t cardinality = 0;
382 for (size_t i = 0; i < match.size(); ++i)
383 if (match[i] != No_Vertex and i < static_cast<size_t>(match[i]))
384 ++cardinality;
385
386 return cardinality;
387 }
388
391 {
392 return match;
393 }
394
396 [[nodiscard]] Arc * get_pair_arc(size_t u, size_t v) const noexcept
397 {
398 const Pair_Key key = normalized_pair(u, v);
399 const auto * item = pair_to_arc.search(key);
400 if (item == nullptr)
401 return nullptr;
402 return item->second;
403 }
404 };
405 } // namespace blossom_detail
406
407
430 template <class GT, class SA = Dft_Show_Arc<GT>>
432 (const GT & g, DynDlist<typename GT::Arc *> & matching, SA sa = SA())
433 {
435 << "compute_maximum_cardinality_general_matching(): g is a digraph";
436
437 matching.empty();
438
440 const size_t cardinality = matcher.solve();
441
442 const auto & mate = matcher.get_match_vector();
443 for (size_t i = 0; i < mate.size(); ++i)
444 if (mate[i] != -1 and i < static_cast<size_t>(mate[i]))
445 {
446 auto *arc = matcher.get_pair_arc(i, static_cast<size_t>(mate[i]));
447 ah_runtime_error_unless(arc != nullptr)
448 << "Blossom internal error: missing arc for matched pair";
449 matching.append(arc);
450 }
451
452 return cardinality;
453 }
454
455
463 template <class GT, class SA = Dft_Show_Arc<GT>>
469
470
478 template <class GT, class SA = Dft_Show_Arc<GT>>
480 {
482
483 public:
485 : sa(std::move(__sa))
486 {
487 // empty
488 }
489
500 };
501} // namespace Aleph
502
503# 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:250
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:351
void empty() noexcept
Empties the container.
Definition tpl_array.H:336
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:315
Functor wrapper for maximum cardinality general matching.
Definition Blossom.H:480
size_t operator()(const GT &g, DynDlist< typename GT::Arc * > &matching)
Computes a maximum matching.
Definition Blossom.H:496
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:1206
void augment_matching(const long endpoint)
Augment the matching along the path ending at endpoint.
Definition Blossom.H:316
void build_adjacency()
Build a simplified adjacency list for faster traversal.
Definition Blossom.H:149
Arc * get_pair_arc(size_t u, size_t v) const noexcept
Returns the arc connecting node indices u and v.
Definition Blossom.H:396
void build_node_index()
Index nodes from 0 to n-1 and set up the cookie-based mapping.
Definition Blossom.H:133
size_t solve()
Execute the matching algorithm.
Definition Blossom.H:367
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:195
Edmonds_Blossom_Matcher(const GT &graph, SA __sa=SA())
Initialize the matcher with a graph and an optional filter.
Definition Blossom.H:334
const Array< long > & get_match_vector() const noexcept
Returns the match vector (mate of each node index).
Definition Blossom.H:390
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:226
DynMapTree< Pair_Key, Arc * > pair_to_arc
Definition Blossom.H:116
long find_augmenting_path(const size_t root)
Search for an augmenting path starting from root.
Definition Blossom.H:250
std::pair< size_t, size_t > Pair_Key
Definition Blossom.H:106
static Pair_Key normalized_pair(size_t u, size_t v) noexcept
Definition Blossom.H:125
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:737
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
bool is_digraph() const noexcept
Return true if the graph this is directed.
Definition graph-dry.H:657
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:743
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:4060
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:432
#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:465
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
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:1164
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.