46# include <gtest/gtest.h>
76 const DynArray<std::pair<size_t, size_t>> & edges,
80 for (
size_t i = 0; i < n; ++i)
81 static_cast<void>(g.
insert_node(
static_cast<int>(i)));
87 const size_t id =
static_cast<size_t>(p->
get_info());
92 for (
size_t i = 0; i < n; ++i)
94 <<
"build_graph(): node id " << i <<
" not found after insertion";
96 for (
const auto & [u, v] : edges)
108 std::initializer_list<std::pair<size_t, size_t>> edges,
113 for (
const auto & edge : edges)
125 for (
typename GT::Arc_Iterator it(g); it.has_curr(); it.next_ne())
129 for (
auto it =
matching.get_it(); it.has_curr(); it.next_ne())
131 auto * arc = it.get_curr();
140 if (src ==
nullptr or tgt ==
nullptr)
146 if (
used_nodes.contains_or_insert(src).second)
149 if (
used_nodes.contains_or_insert(tgt).second)
157 (
size_t n,
const DynArray<std::pair<size_t, size_t>> & edges)
161 for (
size_t i = 0; i < edges.size(); ++i)
163 auto [u, v] = edges(i);
164 if (u >= n
or v >= n
or u == v)
168 seen.insert(std::make_pair(u, v));
172 for (
const auto & p :
seen)
179 const DynArray<std::pair<size_t, size_t>> & edges)
189 for (
size_t i = 0; i <
simple.size(); ++i)
196 const size_t state_count =
static_cast<size_t>(
uint64_t{1} << n);
204 int &
ans =
memo(
static_cast<size_t>(mask));
208 const size_t i =
static_cast<size_t>(std::countr_zero(mask));
216 const size_t j =
static_cast<size_t>(std::countr_zero(
options));
226 return static_cast<size_t>(solve(
all));
241 const size_t n = left_size + right_size;
243 for (
size_t i = 0; i < left_size; ++i)
244 static_cast<void>(g.
insert_node(
static_cast<int>(i)));
246 for (
size_t i = 0; i < right_size; ++i)
247 static_cast<void>(g.
insert_node(
static_cast<int>(left_size + i)));
253 const size_t id =
static_cast<size_t>(p->
get_info());
258 for (
size_t i = 0; i < n; ++i)
260 <<
"build_bipartite_graph(): node id " << i <<
" not found after insertion";
262 for (
size_t i = 0; i <
edges_lr.size(); ++i)
265 if (
l >= left_size
or r >= right_size)
274 class BlossomMatchingTypedTest :
public ::testing::Test
320 auto g =
build_graph<Graph>(5, {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0}});
336 for (
size_t i = 0; i < 6; ++i)
337 for (
size_t j = i + 1; j < 6; ++j)
338 edges.
append(std::make_pair(i, j));
358 {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0},
359 {1, 5}, {2, 6}, {3, 7}});
396 {{0, 1}, {1, 2}, {2, 0}, {2, 3}, {3, 4},
397 {4, 5}, {5, 6}, {1, 6}});
415 std::mt19937
rng(20260220);
416 std::uniform_real_distribution<double>
coin(0.0, 1.0);
418 for (
size_t n = 2; n <= 10; ++n)
419 for (
int sample = 0; sample < 30; ++sample)
421 const double p = 0.10 + 0.03 *
static_cast<double>(sample);
424 edges.
reserve(n * (n - 1) / 2);
425 for (
size_t u = 0; u < n; ++u)
426 for (
size_t v = u + 1; v < n; ++v)
428 edges.
append(std::make_pair(u, v));
437 <<
"n=" << n <<
" sample=" << sample;
439 <<
"n=" << n <<
" sample=" << sample;
441 <<
"n=" << n <<
" sample=" << sample;
450 std::uniform_real_distribution<double>
coin(0.0, 1.0);
452 for (
size_t left = 1; left <= 6; ++left)
453 for (
size_t right = 1; right <= 6; ++right)
454 for (
int sample = 0; sample < 10; ++sample)
456 const double p = 0.15 + 0.07 *
static_cast<double>(sample);
460 for (
size_t l = 0;
l < left; ++
l)
461 for (
size_t r = 0;
r < right; ++
r)
475 <<
"left=" << left <<
" right=" << right <<
" sample=" << sample;
492 {0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0},
493 {2, 5}, {5, 6}, {6, 7}, {7, 8}, {8, 2},
494 {1, 9}, {4, 10}, {6, 11}
520 {0, 1}, {1, 2}, {2, 0},
521 {0, 3}, {3, 4}, {4, 0},
522 {0, 5}, {5, 6}, {6, 0},
523 {2, 7}, {7, 8}, {8, 9}
541 std::mt19937
rng(0xB10550u);
542 std::uniform_real_distribution<double>
coin(0.0, 1.0);
545 for (
int sample = 0; sample < 12; ++sample)
547 const size_t n = 30 +
static_cast<size_t>(sample % 11);
548 const double p = 0.16 + 0.01 *
static_cast<double>(sample);
551 edges.
reserve(n * (n - 1) / 2);
552 for (
size_t u = 0; u < n; ++u)
553 for (
size_t v = u + 1; v < n; ++v)
555 edges.
append(std::make_pair(u, v));
562 <<
"sample=" << sample <<
" n=" << n;
564 <<
"sample=" << sample <<
" n=" << n;
566 <<
"sample=" << sample <<
" n=" << n;
574 const char *
run_perf = std::getenv(
"ALEPH_RUN_BLOSSOM_PERF");
576 GTEST_SKIP() <<
"Set ALEPH_RUN_BLOSSOM_PERF=1 to run blossom perf regression test";
578 constexpr size_t n = 900;
579 constexpr double p = 0.02;
580 constexpr unsigned seed = 0xB10550u;
588 std::uniform_real_distribution<double>
coin(0.0, 1.0);
591 edges.
reserve(
static_cast<size_t>(n * n * p));
592 for (
size_t u = 0; u < n; ++u)
593 for (
size_t v = u + 1; v < n; ++v)
595 edges.
append(std::make_pair(u, v));
598 edges.
append(std::make_pair(0, 1));
603 const auto t0 = std::chrono::steady_clock::now();
605 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
606 std::chrono::steady_clock::now() -
t0).count();
612 <<
"Blossom perf regression: n=" << n
613 <<
", edges=" << edges.
size()
614 <<
", elapsed_ms=" << elapsed_ms
624 struct Positive_Arc_Filter
627 bool operator()(
Arc * a)
const noexcept
629 return a->get_info() > 0;
652 const size_t cardinality =
654 g,
matching, Positive_Arc_Filter());
666 auto * n0 = g.insert_node(0);
667 auto * n1 = g.insert_node(1);
668 g.insert_arc(n0, n1, 1);
Edmonds' Blossom algorithm for maximum matching in general graphs.
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
TYPED_TEST_SUITE(AStarHeapTest, HeapTypes)
bool verify_matching(const Graph &g, const DynDlist< Graph::Arc * > &matching)
Verifies that a matching is valid:
TYPED_TEST(BlossomMatchingTypedTest, EmptyGraph)
Simple dynamic array with automatic resizing and functional operations.
Generic directed graph (digraph) wrapper template.
size_t size() const noexcept
Return the current dimension of array.
T & append()
Allocate a new entry to the end of array.
bool is_empty() const noexcept
Return true if the array is empty.
void reserve(const size_t l, const size_t r)
Allocate a range of entries.
Dynamic doubly linked list with O(1) size and bidirectional access.
Dynamic set backed by balanced binary search trees with automatic memory management.
Arc for graphs implemented with simple adjacency lists.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Graph class implemented with singly-linked adjacency lists.
Open addressing hash table with linear probing collision resolution.
NodeInfo & get_info() noexcept
Return a modifiable reference to the data contained in the node.
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
void for_each_node(Operation &operation) const
Unconditionally traverse all the nodes of graph and on each one perform an operation.
Key * insert(const Key &key)
Inserts a key into the hash table (copy version).
DynArray< Graph::Node * > nodes
Main namespace for Aleph-w library functions.
bool all(Container &container, Operation &operation)
Return true if all elements satisfy a predicate.
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[]
Arc of graph implemented with double-linked adjacency lists.
Array-based graph implementation.
Dynamic array container with automatic resizing.
Bipartite graph detection and 2-coloring.
Lazy and scalable dynamic array implementation.
Dynamic set implementations based on balanced binary search trees.
Generic graph and digraph implementations.
Open addressing hash table with linear probing.
Simple graph implementation with adjacency lists.