Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
weighted_blossom_test.cc
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
45#include <gtest/gtest.h>
46
47#include <algorithm>
48#include <cstdint>
49#include <functional>
50#include <limits>
51#include <random>
52#include <stdexcept>
53#include <tuple>
54#include <unordered_map>
55#include <unordered_set>
56#include <utility>
57#include <vector>
58
59#include <Blossom_Weighted.H>
60#include <tpl_agraph.H>
61#include <tpl_graph.H>
62#include <tpl_sgraph.H>
63
64using namespace Aleph;
65
66namespace
67{
68 using Weighted_Edge = std::tuple<size_t, size_t, long long>;
69 using List_Backend =
71 using SGraph_Backend =
73 using AGraph_Backend =
75
76 struct Objective
77 {
78 size_t cardinality = 0;
79 long long total_weight = 0;
80
81 bool operator==(const Objective & other) const noexcept
82 {
83 return cardinality == other.cardinality
84 and total_weight == other.total_weight;
85 }
86 };
87
88
89 template <class GT>
90 GT build_graph(size_t n, const std::vector<Weighted_Edge> & edges)
91 {
92 GT g;
93 std::vector<typename GT::Node *> nodes;
94 nodes.reserve(n);
95
96 for (size_t i = 0; i < n; ++i)
97 nodes.push_back(g.insert_node(static_cast<int>(i)));
98
99 for (const auto & [u, v, w] : edges)
100 {
101 if (u >= n or v >= n)
102 continue;
103 g.insert_arc(nodes[u], nodes[v], w);
104 }
105
106 return g;
107 }
108
109
110 template <class GT>
111 bool verify_matching(const GT & g,
113 {
114 std::unordered_set<typename GT::Arc *> arcs_in_graph;
115 for (typename GT::Arc_Iterator it(g); it.has_curr(); it.next_ne())
116 arcs_in_graph.insert(it.get_curr());
117
118 std::unordered_set<typename GT::Node *> used_nodes;
119 for (auto it = matching.get_it(); it.has_curr(); it.next_ne())
120 {
121 auto * arc = it.get_curr();
122 if (arc == nullptr)
123 return false;
124
125 if (arcs_in_graph.find(arc) == arcs_in_graph.end())
126 return false;
127
128 auto * src = g.get_src_node(arc);
129 auto * tgt = g.get_tgt_node(arc);
130 if (src == nullptr or tgt == nullptr or src == tgt)
131 return false;
132
133 if (not used_nodes.insert(src).second)
134 return false;
135
136 if (not used_nodes.insert(tgt).second)
137 return false;
138 }
139
140 return true;
141 }
142
143
144 bool better(const Objective & lhs,
145 const Objective & rhs,
146 bool max_cardinality) noexcept
147 {
148 if (max_cardinality)
149 {
150 if (lhs.cardinality != rhs.cardinality)
151 return lhs.cardinality > rhs.cardinality;
152 return lhs.total_weight > rhs.total_weight;
153 }
154
155 if (lhs.total_weight != rhs.total_weight)
156 return lhs.total_weight > rhs.total_weight;
157
158 return lhs.cardinality > rhs.cardinality;
159 }
160
161
162 std::vector<Weighted_Edge>
163 normalize_simple_edges(size_t n, const std::vector<Weighted_Edge> & edges)
164 {
165 using Pair = std::pair<size_t, size_t>;
166 struct Pair_Hash
167 {
168 size_t operator()(const Pair & p) const noexcept
169 {
170 const size_t h1 = std::hash<size_t>{}(p.first);
171 const size_t h2 = std::hash<size_t>{}(p.second);
172 return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
173 }
174 };
175
176 std::unordered_map<Pair, long long, Pair_Hash> best;
177 best.reserve(edges.size() * 2 + 1);
178
179 for (auto [u, v, w] : edges)
180 {
181 if (u >= n or v >= n or u == v)
182 continue;
183
184 if (u > v)
185 std::swap(u, v);
186
187 const Pair key{u, v};
188 const auto it = best.find(key);
189 if (it == best.end() or w > it->second)
190 best[key] = w;
191 }
192
193 std::vector<Weighted_Edge> result;
194 result.reserve(best.size());
195 for (const auto & kv : best)
196 result.emplace_back(kv.first.first, kv.first.second, kv.second);
197
198 std::sort(result.begin(), result.end(),
199 [](const Weighted_Edge & a, const Weighted_Edge & b)
200 {
201 if (std::get<0>(a) != std::get<0>(b))
202 return std::get<0>(a) < std::get<0>(b);
203 if (std::get<1>(a) != std::get<1>(b))
204 return std::get<1>(a) < std::get<1>(b);
205 return std::get<2>(a) < std::get<2>(b);
206 });
207
208 return result;
209 }
210
211
212 Objective exact_weighted_matching_optimum(size_t n,
213 const std::vector<Weighted_Edge> & edges,
214 bool max_cardinality)
215 {
216 if (n == 0)
217 return Objective{0, 0};
218
219 // Exhaustive DP for validation in tests.
220 ah_runtime_error_if(n > 24)
221 << "exact_weighted_matching_optimum supports n <= 24";
222
223 constexpr long long NO_EDGE = std::numeric_limits<long long>::min() / 4;
224 std::vector<std::vector<long long>> w(n, std::vector<long long>(n, NO_EDGE));
225
226 for (auto [u, v, wt] : normalize_simple_edges(n, edges))
227 {
228 w[u][v] = wt;
229 w[v][u] = wt;
230 }
231
232 const uint64_t full_mask = (n == 64)
233 ? std::numeric_limits<uint64_t>::max()
234 : ((uint64_t{1} << n) - 1);
235
236 const size_t state_count = static_cast<size_t>(full_mask + 1);
237 std::vector<Objective> memo(state_count);
238 std::vector<char> seen(state_count, 0);
239
240 std::function<Objective(uint64_t)> solve = [&](uint64_t mask) -> Objective
241 {
242 if (mask == 0)
243 return Objective{0, 0};
244
245 const size_t pos = static_cast<size_t>(mask);
246 if (seen[pos])
247 return memo[pos];
248
249 const size_t i = static_cast<size_t>(__builtin_ctzll(mask));
250 const uint64_t rest = mask & ~(uint64_t{1} << i);
251
252 Objective best = solve(rest); // i unmatched
253
255 while (options != 0)
256 {
257 const size_t j = static_cast<size_t>(__builtin_ctzll(options));
258 options &= (options - 1);
259
260 if (w[i][j] == NO_EDGE)
261 continue;
262
263 Objective candidate = solve(rest & ~(uint64_t{1} << j));
264 ++candidate.cardinality;
265 candidate.total_weight += w[i][j];
266
267 if (better(candidate, best, max_cardinality))
268 best = candidate;
269 }
270
271 seen[pos] = 1;
272 memo[pos] = best;
273 return best;
274 };
275
276 return solve(full_mask);
277 }
278
279
280 template <class GT>
282 {
283 Objective obj;
284
285 for (auto it = matching.get_it(); it.has_curr(); it.next_ne())
286 {
287 auto * arc = it.get_curr();
288 obj.total_weight += static_cast<long long>(arc->get_info());
289 ++obj.cardinality;
290 }
291
292 return obj;
293 }
294
295
296 template <class GT>
297 Objective solve_weighted(const GT & g,
299 bool max_cardinality)
300 {
302 g,
303 matching,
304 Dft_Dist<GT>(),
307
308 EXPECT_EQ(res.cardinality, matching.size());
310 EXPECT_EQ(res.cardinality, obj.cardinality);
311 EXPECT_EQ(res.total_weight, obj.total_weight);
312
313 return obj;
314 }
315
316
317 template <class GT>
318 std::vector<std::pair<size_t, size_t>>
321 {
322 std::vector<std::pair<size_t, size_t>> pairs;
323 pairs.reserve(matching.size());
324
325 for (auto it = matching.get_it(); it.has_curr(); it.next_ne())
326 {
327 auto * arc = it.get_curr();
328 size_t u = static_cast<size_t>(g.get_src_node(arc)->get_info());
329 size_t v = static_cast<size_t>(g.get_tgt_node(arc)->get_info());
330 if (u > v)
331 std::swap(u, v);
332 pairs.emplace_back(u, v);
333 }
334
335 std::sort(pairs.begin(), pairs.end());
336 return pairs;
337 }
338
339
340 struct Solve_Snapshot
341 {
342 Objective objective;
343 std::vector<std::pair<size_t, size_t>> pairs;
344 };
345
346
347 template <class GT>
348 Solve_Snapshot solve_backend(size_t n,
349 const std::vector<Weighted_Edge> & edges,
350 bool max_cardinality)
351 {
352 GT g = build_graph<GT>(n, edges);
353
355 const Objective objective = solve_weighted(g, matching, max_cardinality);
356
358
359 return Solve_Snapshot{
360 objective,
362 }
363
364
365 template <class GT>
366 void assert_matches_exact(size_t n,
367 const std::vector<Weighted_Edge> & edges,
368 bool max_cardinality)
369 {
370 GT g = build_graph<GT>(n, edges);
371
373 const Objective got = solve_weighted(g, matching, max_cardinality);
374
376
378 n, edges, max_cardinality);
379
381 }
382
383
384 template <class GT>
385 class WeightedBlossomTypedTest : public ::testing::Test
386 {
387 };
388
389 using GraphBackends = ::testing::Types<
393
394 TYPED_TEST_SUITE(WeightedBlossomTypedTest, GraphBackends);
395
396} // namespace
397
398
399TYPED_TEST(WeightedBlossomTypedTest, EmptyGraph)
400{
401 using Graph = TypeParam;
402
403 Graph g;
405
406 const auto result = blossom_maximum_weight_matching<Graph>(g, matching);
407
408 EXPECT_EQ(result.cardinality, 0u);
409 EXPECT_EQ(result.total_weight, 0);
410 EXPECT_TRUE(matching.is_empty());
411}
412
413
414TYPED_TEST(WeightedBlossomTypedTest, SinglePositiveEdge)
415{
416 using Graph = TypeParam;
417
418 assert_matches_exact<Graph>(2, {{0, 1, 7}}, false);
419}
420
421
422TYPED_TEST(WeightedBlossomTypedTest, ParallelEdgesAndLoops)
423{
424 using Graph = TypeParam;
425
426 const std::vector<Weighted_Edge> edges = {
427 {0, 0, 99},
428 {0, 1, 5},
429 {0, 1, 12},
430 {1, 2, 8},
431 {2, 3, 9},
432 {1, 3, 2}};
433
434 assert_matches_exact<Graph>(4, edges, false);
435 assert_matches_exact<Graph>(4, edges, true);
436}
437
438
440{
441 using Graph = TypeParam;
442
443 const std::vector<Weighted_Edge> edges = {
444 {0, 1, -1},
445 {1, 2, -2},
446 {2, 3, -1},
447 {0, 3, -2},
448 {0, 2, -3}};
449
450 assert_matches_exact<Graph>(4, edges, false);
451 assert_matches_exact<Graph>(4, edges, true);
452}
453
454
456{
457 using Graph = TypeParam;
458
459 // Selected cases from known weighted blossom stress regressions.
460 const std::vector<std::pair<size_t, std::vector<Weighted_Edge>>> cases = {
461 {4, {{1, 2, 8}, {1, 3, 9}, {2, 3, 10}, {3, 4, 7}}},
462 {6, {{1, 2, 9}, {1, 3, 8}, {2, 3, 10}, {1, 4, 5}, {4, 5, 4}, {1, 6, 3}}},
463 {8,
464 {{1, 2, 23}, {1, 5, 22}, {1, 6, 15}, {2, 3, 25},
465 {3, 4, 22}, {4, 5, 25}, {4, 8, 14}, {5, 7, 13}}},
466 {10,
467 {{1, 2, 45}, {1, 5, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 50},
468 {1, 6, 30}, {3, 9, 35}, {4, 8, 28}, {5, 7, 26}, {9, 10, 5}}},
469 {11,
470 {{1, 2, 40}, {1, 3, 40}, {2, 3, 60}, {2, 4, 55}, {3, 5, 55},
471 {4, 5, 50}, {1, 8, 15}, {5, 7, 30}, {7, 6, 10}, {8, 10, 10}, {4, 9, 30}}}
472 };
473
474 for (const auto & [n, edges_one_based] : cases)
475 {
476 std::vector<Weighted_Edge> edges_zero_based;
477 edges_zero_based.reserve(edges_one_based.size());
478
479 for (const auto & [u, v, w] : edges_one_based)
480 edges_zero_based.emplace_back(u - 1, v - 1, w);
481
484 }
485}
486
487
489{
490 using Graph = TypeParam;
491
492 std::mt19937_64 rng(0xC0FFEE1234ULL);
493 std::uniform_int_distribution<int> n_dist(1, 11);
494 std::uniform_real_distribution<double> p_dist(0.0, 1.0);
495 std::uniform_int_distribution<int> w_dist(-10, 24);
496
497 for (size_t iter = 0; iter < 180; ++iter)
498 {
499 const size_t n = static_cast<size_t>(n_dist(rng));
500 std::vector<Weighted_Edge> edges;
501
502 for (size_t u = 0; u < n; ++u)
503 for (size_t v = u + 1; v < n; ++v)
504 {
505 if (p_dist(rng) < 0.45)
506 {
507 edges.emplace_back(u, v, static_cast<long long>(w_dist(rng)));
508
509 // Add occasional parallel edge with different weight.
510 if (p_dist(rng) < 0.18)
511 edges.emplace_back(u, v, static_cast<long long>(w_dist(rng)));
512 }
513 }
514
515 // Rare loops should be ignored by the algorithm.
516 if (n > 0 and p_dist(rng) < 0.2)
517 edges.emplace_back(static_cast<size_t>(rng() % n),
518 static_cast<size_t>(rng() % n),
519 static_cast<long long>(w_dist(rng)));
520
521 assert_matches_exact<Graph>(n, edges, false);
522 assert_matches_exact<Graph>(n, edges, true);
523 }
524}
525
526
528{
529 const std::vector<Weighted_Edge> edges = {
530 {0, 1, 14},
531 {1, 2, 13},
532 {2, 0, 12},
533 {2, 3, 30},
534 {4, 5, 11},
535 {5, 6, 10},
536 {4, 6, 9},
537 {0, 4, 8},
538 {1, 5, 7},
539 {3, 6, 6}
540 };
541
542 for (bool max_cardinality : {false, true})
543 {
544 const Solve_Snapshot list_res = solve_backend<List_Backend>(
545 7, edges, max_cardinality);
546 const Solve_Snapshot sgraph_res = solve_backend<SGraph_Backend>(
547 7, edges, max_cardinality);
548 const Solve_Snapshot agraph_res = solve_backend<AGraph_Backend>(
549 7, edges, max_cardinality);
550
552 7, edges, max_cardinality);
553
554 EXPECT_EQ(list_res.objective, expected);
555 EXPECT_EQ(sgraph_res.objective, expected);
556 EXPECT_EQ(agraph_res.objective, expected);
557
558 EXPECT_EQ(list_res.pairs, sgraph_res.pairs);
559 EXPECT_EQ(list_res.pairs, agraph_res.pairs);
560 }
561}
562
563
565{
566 std::mt19937_64 rng(0xB10550ULL);
567 std::uniform_int_distribution<int> n_dist(1, 10);
568 std::uniform_real_distribution<double> p_dist(0.0, 1.0);
569 std::uniform_int_distribution<int> w_dist(-12, 25);
570
571 for (size_t iter = 0; iter < 140; ++iter)
572 {
573 const size_t n = static_cast<size_t>(n_dist(rng));
574 std::vector<Weighted_Edge> edges;
575
576 for (size_t u = 0; u < n; ++u)
577 for (size_t v = u + 1; v < n; ++v)
578 {
579 if (p_dist(rng) < 0.42)
580 {
581 edges.emplace_back(u, v, static_cast<long long>(w_dist(rng)));
582 if (p_dist(rng) < 0.2)
583 edges.emplace_back(u, v, static_cast<long long>(w_dist(rng)));
584 }
585 }
586
587 if (n > 0 and p_dist(rng) < 0.3)
588 edges.emplace_back(static_cast<size_t>(rng() % n),
589 static_cast<size_t>(rng() % n),
590 static_cast<long long>(w_dist(rng)));
591
592 for (bool max_cardinality : {false, true})
593 {
594 const Solve_Snapshot list_res = solve_backend<List_Backend>(
595 n, edges, max_cardinality);
596 const Solve_Snapshot sgraph_res = solve_backend<SGraph_Backend>(
597 n, edges, max_cardinality);
598 const Solve_Snapshot agraph_res = solve_backend<AGraph_Backend>(
599 n, edges, max_cardinality);
600
602 n, edges, max_cardinality);
603
604 EXPECT_EQ(list_res.objective, expected);
605 EXPECT_EQ(sgraph_res.objective, expected);
606 EXPECT_EQ(agraph_res.objective, expected);
607 EXPECT_EQ(list_res.objective, sgraph_res.objective);
608 EXPECT_EQ(list_res.objective, agraph_res.objective);
609 }
610 }
611}
612
613
614namespace
615{
616 struct Positive_Arc_Filter
617 {
618 template <class Arc>
619 bool operator()(Arc * arc) const noexcept
620 {
621 return arc->get_info() > 0;
622 }
623 };
624}
625
626
628{
630
631 Graph g;
632 auto * n0 = g.insert_node(0);
633 auto * n1 = g.insert_node(1);
634 auto * n2 = g.insert_node(2);
635 auto * n3 = g.insert_node(3);
636
637 g.insert_arc(n0, n1, 5);
638 g.insert_arc(n1, n2, -100);
639 g.insert_arc(n2, n3, 6);
640 g.insert_arc(n0, n3, -50);
641
643 const auto result = blossom_maximum_weight_matching<
644 Graph,
646 Positive_Arc_Filter>(g,
647 matching,
649 Positive_Arc_Filter(),
650 false);
651
652 EXPECT_EQ(result.cardinality, 2u);
653 EXPECT_EQ(result.total_weight, 11);
655}
656
657
659{
661
662 Digraph g;
663 auto * n0 = g.insert_node(0);
664 auto * n1 = g.insert_node(1);
665 g.insert_arc(n0, n1, 42);
666
668
670 std::domain_error);
671}
672
673
675{
677
678 const std::vector<Weighted_Edge> edges = {
679 {0, 1, 2},
680 {0, 4, 3},
681 {1, 2, 7},
682 {1, 5, 2},
683 {2, 3, 9},
684 {2, 5, 4},
685 {3, 4, 8},
686 {3, 5, 4}
687 };
688
689 Graph g = build_graph<Graph>(6, edges);
690
692 const Objective pure_weight = solve_weighted(g, weight_matching, false);
693
695 const Objective max_card = solve_weighted(g, card_matching, true);
696
699
700 EXPECT_GE(max_card.cardinality, pure_weight.cardinality);
701
702 const Objective exact_weight = exact_weighted_matching_optimum(6, edges, false);
703 const Objective exact_card = exact_weighted_matching_optimum(6, edges, true);
704
707}
708
709
711{
712 namespace mw = blossom_weighted_detail::mwmatching;
713
715 edges.reserve(3);
716 edges.append(mw::Edge<double>(0, 1, 1.5));
717 edges.append(mw::Edge<double>(1, 2, 2.0));
718 edges.append(mw::Edge<double>(0, 2, 1.0));
719
720 const Array<mw::VertexPair> sol = mw::maximum_weight_matching(edges);
721 ASSERT_EQ(sol.size(), 1u);
722 const auto [u, v] = sol[0];
723 EXPECT_TRUE((u == 1 and v == 2) or (u == 2 and v == 1));
724
727 mw::Edge<double>(0, 1, std::numeric_limits<double>::quiet_NaN()));
728 EXPECT_THROW((mw::maximum_weight_matching(nan_edges)),
729 std::invalid_argument);
730 EXPECT_THROW((mw::adjust_weights_for_maximum_cardinality_matching(nan_edges)),
731 std::invalid_argument);
732
735 mw::Edge<double>(0, 1, std::numeric_limits<double>::infinity()));
736 EXPECT_THROW((mw::maximum_weight_matching(inf_edges)),
737 std::invalid_argument);
738}
739
740
742{
743 namespace mw = blossom_weighted_detail::mwmatching;
744
746 self_loop.append(mw::Edge<long long>(0, 0, 7));
747 EXPECT_THROW((mw::maximum_weight_matching(self_loop)),
748 std::invalid_argument);
749
750 Array<mw::Edge<long long>> duplicate;
751 duplicate.append(mw::Edge<long long>(0, 1, 8));
752 duplicate.append(mw::Edge<long long>(1, 0, 9));
753 EXPECT_THROW((mw::maximum_weight_matching(duplicate)),
754 std::invalid_argument);
755}
756
757
759{
760 namespace mw = blossom_weighted_detail::mwmatching;
761
762 const long long max_safe = std::numeric_limits<long long>::max() / 4;
763 const long long min_safe = std::numeric_limits<long long>::min() / 4;
764
766 too_large_weight.append(mw::Edge<long long>(0, 1, max_safe + 1));
767 EXPECT_THROW((mw::maximum_weight_matching(too_large_weight)),
768 std::invalid_argument);
769
771 too_small_weight.append(mw::Edge<long long>(0, 1, min_safe - 1));
772 EXPECT_THROW((mw::adjust_weights_for_maximum_cardinality_matching(
774 std::invalid_argument);
775
777 huge_range.append(mw::Edge<long long>(0, 1, 0));
778 huge_range.append(mw::Edge<long long>(0, 2, max_safe));
779 EXPECT_THROW((mw::adjust_weights_for_maximum_cardinality_matching(huge_range)),
780 std::invalid_argument);
781}
782
783
785{
787 (blossom_weighted_detail::to_ll_checked<unsigned long long>(
788 std::numeric_limits<unsigned long long>::max())),
789 std::overflow_error);
790
792 (blossom_weighted_detail::to_ll_checked<long long>(
793 std::numeric_limits<long long>::max())));
794
796 (blossom_weighted_detail::to_ll_checked<long long>(
797 std::numeric_limits<long long>::min())));
798
800 (blossom_weighted_detail::to_ll_checked<unsigned long long>(
801 static_cast<unsigned long long>(
802 std::numeric_limits<long long>::max()))));
803
805 (blossom_weighted_detail::to_ll_checked<long long>(0LL)));
806}
807
808
810{
811 std::mt19937_64 rng(0x5EED5EED1234ULL);
812 std::uniform_int_distribution<int> n_dist(45, 85);
813 std::uniform_real_distribution<double> p_dist(0.0, 1.0);
814 std::uniform_int_distribution<int> w_dist(-40, 120);
815
816 for (size_t iter = 0; iter < 8; ++iter)
817 {
818 const size_t n = static_cast<size_t>(n_dist(rng));
819 std::vector<Weighted_Edge> edges;
820 edges.reserve((n * n) / 5);
821
822 for (size_t u = 0; u < n; ++u)
823 for (size_t v = u + 1; v < n; ++v)
824 {
825 if (p_dist(rng) < 0.14)
826 {
827 edges.emplace_back(u, v, static_cast<long long>(w_dist(rng)));
828 if (p_dist(rng) < 0.05)
829 edges.emplace_back(u, v, static_cast<long long>(w_dist(rng)));
830 }
831 }
832
833 const Solve_Snapshot list_weight = solve_backend<List_Backend>(
834 n, edges, false);
835 const Solve_Snapshot sgraph_weight = solve_backend<SGraph_Backend>(
836 n, edges, false);
837 const Solve_Snapshot agraph_weight = solve_backend<AGraph_Backend>(
838 n, edges, false);
839
840 EXPECT_EQ(list_weight.objective, sgraph_weight.objective);
841 EXPECT_EQ(list_weight.objective, agraph_weight.objective);
842 EXPECT_LE(list_weight.objective.cardinality, n / 2);
843
844 if (iter < 3)
845 {
846 const Solve_Snapshot list_card = solve_backend<List_Backend>(
847 n, edges, true);
848 const Solve_Snapshot sgraph_card = solve_backend<SGraph_Backend>(
849 n, edges, true);
850 const Solve_Snapshot agraph_card = solve_backend<AGraph_Backend>(
851 n, edges, true);
852
853 EXPECT_EQ(list_card.objective, sgraph_card.objective);
854 EXPECT_EQ(list_card.objective, agraph_card.objective);
855 EXPECT_LE(list_card.objective.cardinality, n / 2);
856 }
857 }
858}
Maximum-weight matching in general undirected graphs.
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
Definition ah-errors.H:266
TYPED_TEST_SUITE(AStarHeapTest, HeapTypes)
WeightedDigraph::Arc Arc
List_Graph< Node, Arc > Graph
bool verify_matching(const Graph &g, const DynDlist< Graph::Arc * > &matching)
Verifies that a matching is valid:
long double w
Definition btreepic.C:153
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
Default distance accessor for arc weights.
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3854
Dynamic doubly linked list with O(1) size and bidirectional access.
Arc for graphs implemented with simple adjacency lists.
Definition tpl_sgraph.H:197
Graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:428
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Graph class implemented with singly-linked adjacency lists.
Definition tpl_sgraph.H:274
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:737
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:743
#define TEST(name)
static mt19937 rng
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
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().
static Geom_Number objective(const Point &p)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
bool operator==(const DynList< T > &l1, const DynList< T > &l2)
Equality operator for DynList.
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.
static struct argp_option options[]
Definition ntreepic.C:1886
#define LL
Definition ran_array.c:24
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Array-based graph implementation.
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.
TYPED_TEST(WeightedBlossomTypedTest, EmptyGraph)