Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Blossom_Weighted.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
80#ifndef BLOSSOM_WEIGHTED_H
81#define BLOSSOM_WEIGHTED_H
82
83# include <ah-graph-concepts.H>
84
85#include <algorithm>
86#include <cstddef>
87#include <cstdint>
88#include <functional>
89#include <limits>
90#include <type_traits>
91#include <utility>
92
93#include <ah-errors.H>
94#include <ahSort.H>
95#include <cookie_guard.H>
96#include <tpl_array.H>
97#include <tpl_dynDlist.H>
98#include <tpl_graph.H>
99#include <tpl_graph_utils.H>
100
102
103namespace Aleph {
111template <typename Weight_Type = long long>
117
118namespace blossom_weighted_detail {
122template <typename T>
124{
125 static_assert(std::is_integral_v<T>, "Blossom_Weighted requires integral arc weights");
126
127 if constexpr (std::is_signed_v<T>)
128 {
129 using Common = std::common_type_t<T, long long>;
130 constexpr auto ll_max = std::numeric_limits<long long>::max();
131 constexpr auto ll_min = std::numeric_limits<long long>::min();
132
133 const auto v = static_cast<Common>(value);
134 ah_overflow_error_if(v > static_cast<Common>(ll_max) or v < static_cast<Common>(ll_min))
135 << "Weight cannot be represented as long long";
136 }
137 else
138 {
139 using Common = std::common_type_t<T, long long>;
140 constexpr auto ll_max = std::numeric_limits<long long>::max();
141
142 const auto v = static_cast<Common>(value);
143 ah_overflow_error_if(v > static_cast<Common>(ll_max))
144 << "Weight cannot be represented as long long";
145 }
146
147 return static_cast<long long>(value);
148}
149} // namespace blossom_weighted_detail
150
177template <AlephGraph GT, class Weight = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
179 const GT &g,
181 Weight weight = Weight(),
182 SA sa = SA(),
183 const bool max_cardinality = false)
184{
185 using Node = typename GT::Node;
186 using Arc = typename GT::Arc;
187 using Raw_Weight = std::decay_t<decltype(weight(static_cast<Arc *>(nullptr)))>;
191
192 static_assert(std::is_integral_v<Raw_Weight>, "Blossom_Weighted requires integral arc weights");
193
194 ah_domain_error_if(g.is_digraph()) << "compute_maximum_weight_general_matching(): g is a digraph";
195
196 matching.empty();
197
198 if (g.get_num_nodes() == 0)
200
201 constexpr auto max_vertex = static_cast<size_t>(std::numeric_limits<MwVertex>::max());
203 << "Graph has too many vertices for weighted blossom implementation";
204
205 Cookie_Saver<GT> cookie_saver(g, true, false);
206
207 size_t num_nodes = 0;
208 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne())
209 {
210 Node *p = it.get_curr();
211 ++num_nodes;
212 const auto encoded = num_nodes; // index + 1
213 NODE_COOKIE(p) = reinterpret_cast<void *>(encoded);
214 }
215
216 struct Arc_Record
217 {
218 size_t u = 0;
219 size_t v = 0;
220 long long weight = 0;
221 Arc *arc = nullptr;
222 };
223
226
227 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
228 {
229 Arc *arc = it.get_curr_ne();
230 Node *src = g.get_src_node(arc);
231 Node *tgt = g.get_tgt_node(arc);
232
233 const auto src_idx_plus_one = reinterpret_cast<uintptr_t>(NODE_COOKIE(src));
234 const auto tgt_idx_plus_one = reinterpret_cast<uintptr_t>(NODE_COOKIE(tgt));
235
237 << "Weighted blossom internal node index mapping is invalid";
238
239 auto u = src_idx_plus_one - 1;
240 auto v = tgt_idx_plus_one - 1;
241 if (u == v)
242 continue; // Loops are irrelevant to matchings.
243
244 if (u > v)
245 std::swap(u, v);
246
247 const long long w = blossom_weighted_detail::to_ll_checked(weight(arc));
248 records.append(Arc_Record{u, v, w, arc});
249 }
250
251 if (records.is_empty())
253
254 struct Arc_Record_Less
255 {
256 bool operator()(const Arc_Record &a, const Arc_Record &b) const noexcept
257 {
258 if (a.u != b.u)
259 return a.u < b.u;
260 if (a.v != b.v)
261 return a.v < b.v;
262 return a.weight > b.weight;
263 }
264 };
266
268 best_edges.reserve(records.size());
269 for (size_t i = 0; i < records.size();)
270 {
271 const Arc_Record &best = records[i];
272 best_edges.append(best);
273 ++i;
274 while (i < records.size() and records[i].u == best.u and records[i].v == best.v)
275 ++i;
276 }
277
278 Array<MwEdge> edges;
279 edges.reserve(best_edges.size());
280 for (const Arc_Record &e : best_edges)
281 edges.append(MwEdge(static_cast<MwVertex>(e.u), static_cast<MwVertex>(e.v), e.weight));
282
284 if (max_cardinality)
287 else
288 solver_edges = edges;
289
290 const Array<MwPair> matched_pairs =
292
293 long long total_weight = 0;
294 size_t cardinality = 0;
295
296 auto find_edge = [&best_edges](size_t u, size_t v) -> const Arc_Record *
297 {
298 size_t lo = 0;
299 size_t hi = best_edges.size();
300
301 while (lo < hi)
302 {
303 const size_t mid = lo + (hi - lo) / 2;
304 if (const Arc_Record &cur = best_edges[mid]; cur.u < u or (cur.u == u and cur.v < v))
305 lo = mid + 1;
306 else
307 hi = mid;
308 }
309
310 if (lo < best_edges.size())
311 {
312 const Arc_Record &cand = best_edges[lo];
313 if (cand.u == u and cand.v == v)
314 return &cand;
315 }
316
317 return nullptr;
318 };
319
320 for (const auto &[fst, snd] : matched_pairs)
321 {
322 auto u = static_cast<size_t>(fst);
323 auto v = static_cast<size_t>(snd);
324 if (u > v)
325 std::swap(u, v);
326
327 const Arc_Record *arc_info = find_edge(u, v);
328 ah_runtime_error_unless(arc_info != nullptr)
329 << "Weighted blossom returned an unknown matched pair";
330
331 matching.append(arc_info->arc);
332#if defined(_MSC_VER) && !defined(__clang__)
333 // MSVC has no __int128; detect long long overflow without it.
334 {
335 const long long w = arc_info->weight;
336 const long long lo = std::numeric_limits<long long>::min();
337 const long long hi = std::numeric_limits<long long>::max();
338 ah_overflow_error_if((w > 0 and total_weight > hi - w) or (w < 0 and total_weight < lo - w))
339 << "Matching total weight overflows long long";
340 total_weight += w;
341 }
342#else
343 const __int128 sum =
344 static_cast<__int128>(total_weight) + static_cast<__int128>(arc_info->weight);
345 ah_overflow_error_if(sum > static_cast<__int128>(std::numeric_limits<long long>::max()) or
346 sum < static_cast<__int128>(std::numeric_limits<long long>::min()))
347 << "Matching total weight overflows long long";
348 total_weight = static_cast<long long>(sum);
349#endif
350 ++cardinality;
351 }
352
353 return Blossom_Weighted_Result<long long>{total_weight, cardinality};
354}
355
373template <AlephGraph GT, class Weight = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
376 Weight weight = Weight(),
377 SA sa = SA(),
378 const bool max_cardinality = false)
379{
381 std::move(sa), max_cardinality);
382}
383
388template <AlephGraph GT, class Weight = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
390{
391 Weight weight_;
392 SA sa_;
393 bool max_cardinality_ = false;
394
395public:
404 SA sa = SA(),
405 const bool max_cardinality = false)
406 : weight_(std::move(weight)), sa_(std::move(sa)), max_cardinality_(max_cardinality)
407 {
408 // empty
409 }
410
427};
428} // namespace Aleph
429
430#endif // BLOSSOM_WEIGHTED_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_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:468
#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.
High-level sorting functions for Aleph containers.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
Internal weighted matching core used by Blossom_Weighted.H.
long double w
Definition btreepic.C:153
int num_nodes
Definition btreepic.C:410
size_t size_t int32_t value
Definition ca-c-api.h:116
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
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 weighted blossom matching.
Compute_Maximum_Weight_General_Matching(Weight weight=Weight(), SA sa=SA(), const bool max_cardinality=false)
Construct the solver with specific options.
Blossom_Weighted_Result< long long > operator()(const GT &g, DynDlist< typename GT::Arc * > &matching)
Compute maximum-weight matching.
RAII guard that saves and restores graph cookies.
Dynamic doubly linked list with O(1) size and bidirectional access.
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
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
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:826
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.
Blossom_Weighted_Result< long long > compute_maximum_weight_general_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, Weight weight=Weight(), SA sa=SA(), const bool max_cardinality=false)
Compute maximum-weight matching in a general undirected graph.
#define NODE_COOKIE(p)
Return the node cookie
Blossom_Weighted_Result< long long > blossom_maximum_weight_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, Weight weight=Weight(), SA sa=SA(), const bool max_cardinality=false)
Alias for compute_maximum_weight_general_matching().
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
Array< Edge< WeightType > > adjust_weights_for_maximum_cardinality_matching(const Array< Edge< WeightType > > &edges_in)
Adjust edge weights to prioritize maximum cardinality matching.
unsigned int VertexId
Type representing the unique ID of a vertex.
std::pair< VertexId, VertexId > VertexPair
Type representing a pair of vertices.
Array< VertexPair > maximum_weight_matching(const Array< Edge< WeightType > > &edges)
Compute a maximum-weighted matching in a general undirected graph.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
DynArray< T > & in_place_sort(DynArray< T > &c, Cmp cmp=Cmp())
Sorts a DynArray in place.
Definition ahSort.H:328
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
STL namespace.
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1165
Result of weighted matching.
Weight_Type total_weight
Sum of matched arc weights.
size_t cardinality
Number of arcs in the matching.
Dynamic array container with automatic resizing.
Dynamic doubly linked list implementation.
Generic graph and digraph implementations.
Utility algorithms and operations for graphs.