Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
Min_Cost_Matching.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
79#ifndef MIN_COST_MATCHING_H
80#define MIN_COST_MATCHING_H
81
82# include <ah-graph-concepts.H>
83
84#include <cstddef>
85#include <limits>
86#include <type_traits>
87#include <utility>
88
89#include <ah-errors.H>
90#include <Blossom_Weighted.H>
91
92namespace Aleph {
100template <typename Cost_Type = long long>
106
116template <typename Cost_Type = long long>
123
124namespace min_cost_matching_detail {
133template <typename T>
135{
136 static_assert(std::is_integral_v<T>, "Min_Cost_Matching requires integral arc costs");
137
138 // Number of value bits in T and in long long (excludes the sign bit).
139 constexpr int T_digits = std::numeric_limits<T>::digits;
140 constexpr int LL_digits = std::numeric_limits<long long>::digits; // 63
141
142 // Upper check: T can hold values > LLONG_MAX when
143 // unsigned T: T_digits >= LL_digits (e.g. unsigned long long: 64 >= 63)
144 // signed T: T_digits > LL_digits (e.g. __int128: 127 > 63)
145 constexpr bool need_upper =
146 std::is_unsigned_v<T> ? (T_digits >= LL_digits) : (T_digits > LL_digits);
147
148 // Lower check: only signed T wider than long long can go below LLONG_MIN.
149 constexpr bool need_lower = std::is_signed_v<T> and (T_digits > LL_digits);
150
151 if constexpr (need_upper)
152 {
153 // MSVC cl has no __int128; compare directly as type T (T is unsigned
154 // and at least 64-bit when need_upper is true, so LLONG_MAX fits in T).
155 ah_overflow_error_if(value > static_cast<T>(std::numeric_limits<long long>::max()))
156 << "Cost cannot be represented as long long";
157 }
158
159 if constexpr (need_lower)
160 {
161 // Only reachable for signed types wider than long long (e.g. __int128).
162 // MSVC cl does not support __int128, so this branch is never
163 // instantiated there; the cast is still guarded for GCC/Clang.
164#if defined(_MSC_VER) && !defined(__clang__)
165 (void) value; // unreachable on MSVC — no type has digits > 63
166#else
167 ah_overflow_error_if(static_cast<__int128>(value) <
168 static_cast<__int128>(std::numeric_limits<long long>::min()))
169 << "Cost cannot be represented as long long";
170#endif
171 }
172
173 return static_cast<long long>(value);
174}
175
179template <class Cost_Accessor>
181{
183
184public:
186 {
187 // empty
188 }
189
190 template <class Arc>
191 long long operator()(Arc *arc) const
192 {
193 const long long c = to_ll_checked(cost_(arc));
194 ah_overflow_error_if(c == std::numeric_limits<long long>::min())
195 << "Minimum-cost matching cannot negate LLONG_MIN cost";
196 return -c;
197 }
198};
199} // namespace min_cost_matching_detail
200
226template <AlephGraph GT, class Cost = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
228 const GT &g,
230 Cost cost = Cost(),
231 SA sa = SA(),
232 const bool max_cardinality = false)
233{
234 ah_domain_error_if(g.is_digraph()) << "compute_minimum_cost_general_matching(): g is a digraph";
235
236 using Arc = typename GT::Arc;
237 using Raw_Cost = std::decay_t<decltype(cost(static_cast<Arc *>(nullptr)))>;
238
239 static_assert(std::is_integral_v<Raw_Cost>, "Min_Cost_Matching requires integral arc costs");
240
241 Cost cost_accessor = std::move(cost);
242
243 const auto weighted_result =
246 std::move(sa), max_cardinality);
247
248 long long total_cost = 0;
249 for (auto it = matching.get_it(); it.has_curr(); it.next_ne())
250 {
251 Arc *arc = it.get_curr();
253#if defined(_MSC_VER) && !defined(__clang__)
254 {
255 const long long lo = std::numeric_limits<long long>::min();
256 const long long hi = std::numeric_limits<long long>::max();
257 ah_overflow_error_if((c > 0 and total_cost > hi - c) or (c < 0 and total_cost < lo - c))
258 << "Minimum-cost matching total cost overflows long long";
259 total_cost += c;
260 }
261#else
262 {
263 const __int128 sum = static_cast<__int128>(total_cost) + static_cast<__int128>(c);
264 ah_overflow_error_if(sum > static_cast<__int128>(std::numeric_limits<long long>::max()) or
265 sum < static_cast<__int128>(std::numeric_limits<long long>::min()))
266 << "Minimum-cost matching total cost overflows long long";
267 total_cost = static_cast<long long>(sum);
268 }
269#endif
270 }
271
273 << "Minimum-cost matching internal cardinality mismatch";
274
276}
277
284template <AlephGraph GT, class Cost = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
287 Cost cost = Cost(),
288 SA sa = SA(),
289 const bool max_cardinality = false)
290{
292 std::move(sa), max_cardinality);
293}
294
323template <AlephGraph GT, class Cost = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
325 const GT &g,
327 Cost cost = Cost(),
328 SA sa = SA())
329{
331 << "compute_minimum_cost_perfect_general_matching(): g is a digraph";
332
333 matching.empty();
334
335 const size_t num_nodes = g.get_num_nodes();
336 if (num_nodes == 0)
338
339 if (num_nodes % 2 == 1)
341
343 g, matching, std::move(cost), std::move(sa), true);
344
345 const size_t expected = num_nodes / 2;
346 if (best.cardinality != expected)
347 {
348 matching.empty();
350 }
351
353}
354
361template <AlephGraph GT, class Cost = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
363 const GT &g,
365 Cost cost = Cost(),
366 SA sa = SA())
367{
369 std::move(sa));
370}
371
376template <AlephGraph GT, class Cost = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
378{
379 Cost cost_;
380 SA sa_;
381 bool max_cardinality_ = false;
382
383public:
385 SA sa = SA(),
386 const bool max_cardinality = false)
387 : cost_(std::move(cost)), sa_(std::move(sa)), max_cardinality_(max_cardinality)
388 {
389 // empty
390 }
391
397};
398
403template <AlephGraph GT, class Cost = Dft_Dist<GT>, ArcFilter<GT> SA = Dft_Show_Arc<GT>>
405{
406 Cost cost_;
407 SA sa_;
408
409public:
410 Compute_Minimum_Cost_Perfect_General_Matching(Cost cost = Cost(), SA sa = SA())
411 : cost_(std::move(cost)), sa_(std::move(sa))
412 {
413 // empty
414 }
415
421};
422} // namespace Aleph
423
424#endif // MIN_COST_MATCHING_H
Maximum-weight matching in general undirected graphs.
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.
WeightedDigraph::Arc Arc
int num_nodes
Definition btreepic.C:410
size_t size_t int32_t value
Definition ca-c-api.h:116
Functor wrapper for minimum-cost general matching.
Compute_Minimum_Cost_General_Matching(Cost cost=Cost(), SA sa=SA(), const bool max_cardinality=false)
Min_Cost_Matching_Result< long long > operator()(const GT &g, DynDlist< typename GT::Arc * > &matching)
Functor wrapper for minimum-cost perfect general matching.
Compute_Minimum_Cost_Perfect_General_Matching(Cost cost=Cost(), SA sa=SA())
Min_Cost_Perfect_Matching_Result< long long > operator()(const GT &g, DynDlist< typename GT::Arc * > &matching)
Dynamic doubly linked list with O(1) size and bidirectional access.
Minimal std::expected-style result type for C++20.
Negated_Cost_Accessor(Cost_Accessor cost=Cost_Accessor())
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
Min_Cost_Perfect_Matching_Result< long long > compute_minimum_cost_perfect_general_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, Cost cost=Cost(), SA sa=SA())
Compute minimum-cost perfect matching in a general undirected graph.
Min_Cost_Matching_Result< long long > compute_minimum_cost_general_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, Cost cost=Cost(), SA sa=SA(), const bool max_cardinality=false)
Compute minimum-cost matching in a general undirected graph.
Min_Cost_Matching_Result< long long > blossom_minimum_cost_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, Cost cost=Cost(), SA sa=SA(), const bool max_cardinality=false)
Alias for compute_minimum_cost_general_matching().
Min_Cost_Perfect_Matching_Result< long long > blossom_minimum_cost_perfect_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, Cost cost=Cost(), SA sa=SA())
Alias for compute_minimum_cost_perfect_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
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
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
STL namespace.
Result of minimum-cost matching.
Cost_Type total_cost
Sum of matched arc costs.
size_t cardinality
Number of arcs in the matching.
Result of minimum-cost perfect matching.
Cost_Type total_cost
Total cost if feasible.
bool feasible
Whether a perfect matching exists.
size_t cardinality
Cardinality (|V|/2 if feasible).