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
31
80#ifndef BLOSSOM_WEIGHTED_H
81#define BLOSSOM_WEIGHTED_H
82
83#include <algorithm>
84#include <cstddef>
85#include <cstdint>
86#include <functional>
87#include <limits>
88#include <type_traits>
89#include <utility>
90
91#include <ah-errors.H>
92#include <ahSort.H>
93#include <cookie_guard.H>
94#include <tpl_array.H>
95#include <tpl_dynDlist.H>
96#include <tpl_graph.H>
97#include <tpl_graph_utils.H>
98
100
101namespace Aleph
102{
110 template <typename Weight_Type = long long>
116
117
118 namespace blossom_weighted_detail
119 {
123 template <typename T>
124 long long to_ll_checked(T value)
125 {
126 static_assert(std::is_integral_v<T>, "Blossom_Weighted requires integral arc weights");
127
128 if constexpr (std::is_signed_v<T>)
129 {
130 using Common = std::common_type_t<T, long long>;
131 constexpr auto ll_max = std::numeric_limits<long long>::max();
132 constexpr auto ll_min = std::numeric_limits<long long>::min();
133
134 const auto v = static_cast<Common>(value);
135 ah_overflow_error_if(v > static_cast<Common>(ll_max)
136 or v < static_cast<Common>(ll_min))
137 << "Weight cannot be represented as long long";
138 }
139 else
140 {
141 using Common = std::common_type_t<T, long long>;
142 constexpr auto ll_max = std::numeric_limits<long long>::max();
143
144 const auto v = static_cast<Common>(value);
145 ah_overflow_error_if(v > static_cast<Common>(ll_max))
146 << "Weight cannot be represented as long long";
147 }
148
149 return static_cast<long long>(value);
150 }
151 } // namespace blossom_weighted_detail
152
153
180 template <class GT,
181 class Weight = Dft_Dist<GT>,
182 class SA = Dft_Show_Arc<GT>>
186 Weight weight = Weight(),
187 SA sa = SA(),
188 const bool max_cardinality = false)
189 {
190 using Node = typename GT::Node;
191 using Arc = typename GT::Arc;
192 using Raw_Weight = std::decay_t<decltype(weight(static_cast<Arc *>(nullptr)))>;
196
197 static_assert(std::is_integral_v<Raw_Weight>,
198 "Blossom_Weighted requires integral arc weights");
199
201 << "compute_maximum_weight_general_matching(): g is a digraph";
202
203 matching.empty();
204
205 if (g.get_num_nodes() == 0)
207
208 constexpr auto max_vertex = static_cast<size_t>(std::numeric_limits<MwVertex>::max());
210 << "Graph has too many vertices for weighted blossom implementation";
211
212 Cookie_Saver<GT> cookie_saver(g, true, false);
213
214 size_t num_nodes = 0;
215 for (Node_Iterator<GT> it(g); it.has_curr(); it.next_ne())
216 {
217 Node *p = it.get_curr();
218 ++num_nodes;
219 const auto encoded = num_nodes; // index + 1
220 NODE_COOKIE(p) = reinterpret_cast<void *>(encoded);
221 }
222
223 struct Arc_Record
224 {
225 size_t u = 0;
226 size_t v = 0;
227 long long weight = 0;
228 Arc *arc = nullptr;
229 };
230
233
234 for (Arc_Iterator<GT, SA> it(g, sa); it.has_curr(); it.next_ne())
235 {
236 Arc *arc = it.get_curr_ne();
237 Node *src = g.get_src_node(arc);
238 Node *tgt = g.get_tgt_node(arc);
239
240 const auto src_idx_plus_one = reinterpret_cast<uintptr_t>(NODE_COOKIE(src));
241 const auto tgt_idx_plus_one = reinterpret_cast<uintptr_t>(NODE_COOKIE(tgt));
242
244 << "Weighted blossom internal node index mapping is invalid";
245
246 auto u = src_idx_plus_one - 1;
247 auto v = tgt_idx_plus_one - 1;
248 if (u == v)
249 continue; // Loops are irrelevant to matchings.
250
251 if (u > v)
252 std::swap(u, v);
253
254 const long long w = blossom_weighted_detail::to_ll_checked(weight(arc));
255 records.append(Arc_Record{u, v, w, arc});
256 }
257
258 if (records.is_empty())
260
261 struct Arc_Record_Less
262 {
263 bool operator()(const Arc_Record & a, const Arc_Record & b) const noexcept
264 {
265 if (a.u != b.u)
266 return a.u < b.u;
267 if (a.v != b.v)
268 return a.v < b.v;
269 return a.weight > b.weight;
270 }
271 };
273
275 best_edges.reserve(records.size());
276 for (size_t i = 0; i < records.size();)
277 {
278 const Arc_Record & best = records[i];
279 best_edges.append(best);
280 ++i;
281 while (i < records.size()
282 and records[i].u == best.u
283 and records[i].v == best.v)
284 ++i;
285 }
286
287 Array<MwEdge> edges;
288 edges.reserve(best_edges.size());
289 for (const Arc_Record & e: best_edges)
290 edges.append(MwEdge(static_cast<MwVertex>(e.u),
291 static_cast<MwVertex>(e.v),
292 e.weight));
293
295 if (max_cardinality)
298 else
299 solver_edges = edges;
300
303
304 long long total_weight = 0;
305 size_t cardinality = 0;
306
307 auto find_edge = [&best_edges](size_t u, size_t v) -> const Arc_Record *
308 {
309 size_t lo = 0;
310 size_t hi = best_edges.size();
311
312 while (lo < hi)
313 {
314 const size_t mid = lo + (hi - lo) / 2;
315 if (const Arc_Record & cur = best_edges[mid]; cur.u < u or (cur.u == u and cur.v < v))
316 lo = mid + 1;
317 else
318 hi = mid;
319 }
320
321 if (lo < best_edges.size())
322 {
323 const Arc_Record & cand = best_edges[lo];
324 if (cand.u == u and cand.v == v)
325 return &cand;
326 }
327
328 return nullptr;
329 };
330
331 for (const auto & [fst, snd]: matched_pairs)
332 {
333 auto u = static_cast<size_t>(fst);
334 auto v = static_cast<size_t>(snd);
335 if (u > v)
336 std::swap(u, v);
337
338 const Arc_Record *arc_info = find_edge(u, v);
339 ah_runtime_error_unless(arc_info != nullptr)
340 << "Weighted blossom returned an unknown matched pair";
341
342 matching.append(arc_info->arc);
343 const __int128 sum = static_cast<__int128>(total_weight)
344 + static_cast<__int128>(arc_info->weight);
345 ah_overflow_error_if(sum > static_cast<__int128>(std::numeric_limits<long long>::max())
346 or 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 ++cardinality;
350 }
351
352 return Blossom_Weighted_Result<long long>{total_weight, cardinality};
353 }
354
355
373 template <class GT,
374 class Weight = Dft_Dist<GT>,
375 class SA = Dft_Show_Arc<GT>>
379 Weight weight = Weight(),
380 SA sa = SA(),
381 const bool max_cardinality = false)
382 {
383 return compute_maximum_weight_general_matching<GT, Weight, SA>(g, matching, std::move(weight), std::move(sa),
385 }
386
387
392 template <class GT,
393 class Weight = Dft_Dist<GT>,
394 class SA = Dft_Show_Arc<GT>>
396 {
397 Weight weight_;
399 bool max_cardinality_ = false;
400
401 public:
410 SA sa = SA(),
411 const bool max_cardinality = false)
412 : weight_(std::move(weight)),
413 sa_(std::move(sa)),
415 {
416 // empty
417 }
418
436 };
437} // namespace Aleph
438
439#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:250
#define ah_overflow_error_if(C)
Throws std::overflow_error if condition holds.
Definition ah-errors.H:463
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
High-level sorting functions for Aleph containers.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
Internal weighted matching core used by Blossom_Weighted.H.
long double w
Definition btreepic.C:153
int num_nodes
Definition btreepic.C:410
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
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 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.
Default distance accessor for arc weights.
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:1206
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
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:784
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.
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().
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.
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.
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:321
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:1164
Result of weighted matching.
Weight_Type total_weight
Sum of matched arc weights.
size_t cardinality
Number of arcs in the matching.
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Dynamic array container with automatic resizing.
Dynamic doubly linked list implementation.
Generic graph and digraph implementations.
Utility algorithms and operations for graphs.