44# include <gtest/gtest.h>
51# include <initializer_list>
72 using Cost_Edge = std::tuple<size_t, size_t, long long>;
83 size_t cardinality = 0;
84 long long total_cost = 0;
88 return cardinality ==
other.cardinality
96 *
os <<
"{card=" <<
obj.cardinality
97 <<
", cost=" <<
obj.total_cost <<
"}";
104 for (
const auto & e :
init)
116 for (
size_t i = 0; i < n; ++i)
119 for (
const auto & [u, v, c] : edges)
121 if (u >= n
or v >= n)
135 for (
typename GT::Arc_Iterator it(g); it.has_curr(); it.next_ne())
139 for (
auto it =
matching.get_it(); it.has_curr(); it.next_ne())
141 auto * arc = it.get_curr();
150 if (src ==
nullptr or tgt ==
nullptr or src == tgt)
166 bool better(
const Objective &
lhs,
167 const Objective &
rhs,
172 if (
lhs.cardinality !=
rhs.cardinality)
173 return lhs.cardinality >
rhs.cardinality;
174 return lhs.total_cost <
rhs.total_cost;
177 if (
lhs.total_cost !=
rhs.total_cost)
178 return lhs.total_cost <
rhs.total_cost;
180 return lhs.cardinality >
rhs.cardinality;
187 using Pair = std::pair<size_t, size_t>;
190 for (
auto [u, v, c] : edges)
192 if (u >= n
or v >= n
or u == v)
198 const Pair key{u, v};
199 auto * it =
best.search(key);
207 result.reserve(
best.size());
208 for (
auto it =
best.get_it(); it.has_curr(); it.next_ne())
210 const auto &
kv = it.get_curr();
211 result.append(std::make_tuple(
kv.first.first,
kv.first.second,
kv.second));
217 if (std::get<0>(a) != std::get<0>(b))
218 return std::get<0>(a) < std::get<0>(b);
219 if (std::get<1>(a) != std::get<1>(b))
220 return std::get<1>(a) < std::get<1>(b);
221 return std::get<2>(a) < std::get<2>(b);
233 return Objective{0, 0};
236 <<
"exact_minimum_cost_matching_optimum supports n <= 24";
238 constexpr long long NO_EDGE = std::numeric_limits<long long>::max() / 4;
240 for (
size_t i = 0; i < n; ++i)
250 const size_t state_count =
static_cast<size_t>(
full_mask + 1);
258 return Objective{0, 0};
260 const size_t pos =
static_cast<size_t>(mask);
264 const size_t i =
static_cast<size_t>(std::countr_zero(mask));
272 const size_t j =
static_cast<size_t>(std::countr_zero(
options));
307 if (
best.cardinality != n / 2)
319 for (
auto it =
matching.get_it(); it.has_curr(); it.next_ne())
321 auto * arc = it.get_curr();
322 obj.total_cost +=
static_cast<long long>(arc->get_info());
454 for (
auto it =
matching.get_it(); it.has_curr(); it.next_ne())
467 auto * n0 = g.insert_node(0);
468 auto * n1 = g.insert_node(1);
469 g.insert_arc(n0, n1, 7);
484 g.
insert_arc(n0, n1, std::numeric_limits<long long>::min());
488 std::overflow_error);
499 const auto huge =
static_cast<unsigned long long>(std::numeric_limits<long long>::max())
505 std::overflow_error);
565 return -arc->get_info();
645 auto * n0 = g.insert_node(0);
646 auto * n1 = g.insert_node(1);
647 g.insert_arc(n0, n1, 4);
753 std::mt19937_64
rng(0xA11CE55ULL);
754 std::uniform_int_distribution<int>
n_half_dist(1, 5);
755 std::uniform_int_distribution<int>
c_dist(-15, 20);
756 std::bernoulli_distribution
edge_coin(0.45);
758 std::bernoulli_distribution
loop_coin(0.08);
764 edges.reserve(n * (n - 1));
766 for (
size_t i = 0; i < n; ++i)
767 for (
size_t j = i + 1; j < n; ++j)
770 edges.append(std::make_tuple(i, j,
static_cast<long long>(
c_dist(
rng))));
772 edges.append(std::make_tuple(i, j,
static_cast<long long>(
c_dist(
rng))));
775 for (
size_t i = 0; i < n; ++i)
777 edges.append(std::make_tuple(i, i,
static_cast<long long>(
c_dist(
rng))));
803 std::mt19937_64
rng(0xC0FFEEULL);
804 std::uniform_int_distribution<int>
n_dist(2, 10);
805 std::uniform_int_distribution<int>
c_dist(-15, 20);
806 std::bernoulli_distribution
edge_coin(0.45);
808 std::bernoulli_distribution
loop_coin(0.10);
812 const size_t n =
static_cast<size_t>(
n_dist(
rng));
814 edges.reserve(n * (n - 1));
816 for (
size_t i = 0; i < n; ++i)
817 for (
size_t j = i + 1; j < n; ++j)
820 edges.append(std::make_tuple(i, j,
static_cast<long long>(
c_dist(
rng))));
822 edges.append(std::make_tuple(i, j,
static_cast<long long>(
c_dist(
rng))));
825 for (
size_t i = 0; i < n; ++i)
827 edges.append(std::make_tuple(i, i,
static_cast<long long>(
c_dist(
rng))));
854 using Clock = std::chrono::steady_clock;
856 constexpr size_t n = 1400;
867 std::mt19937_64
rng(0xBADC0FFEE0DDF00DULL);
868 std::uniform_int_distribution<int>
c_dist(-100, 150);
869 std::bernoulli_distribution
edge_coin(0.015);
871 std::bernoulli_distribution
loop_coin(0.02);
874 edges.reserve(n * 18);
876 for (
size_t i = 0; i < n; ++i)
877 for (
size_t j = i + 1; j < n; ++j)
880 edges.append(std::make_tuple(i, j,
static_cast<long long>(
c_dist(
rng))));
882 edges.append(std::make_tuple(i, j,
static_cast<long long>(
c_dist(
rng))));
885 for (
size_t i = 0; i < n; ++i)
887 edges.append(std::make_tuple(i, i,
static_cast<long long>(
c_dist(
rng))));
889 if (edges.is_empty())
890 edges.append(std::make_tuple(0, 1, 1));
894 for (
const auto & [u, v, c] : edges)
943 const auto general_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
949 <<
"general perf regression: n=" << n
950 <<
", edges=" << edges.size()
959 const auto perfect_elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
973 <<
"perfect perf regression: n=" << n
974 <<
", edges=" << edges.size()
Maximum-weight matching in general undirected graphs.
Minimum-cost matching in general undirected graphs.
#define ah_runtime_error_if(C)
Throws std::runtime_error if condition holds.
bool verify_matching(const Graph &g, const DynDlist< Graph::Arc * > &matching)
Verifies that a matching is valid:
Simple dynamic array with automatic resizing and functional operations.
static Array create(size_t n)
Create an array with n logical elements.
Functor wrapper for minimum-cost general matching.
Functor wrapper for minimum-cost perfect general matching.
Default distance accessor for arc weights.
Generic directed graph (digraph) wrapper template.
Dynamic doubly linked list with O(1) size and bidirectional access.
Generic key-value map implemented on top of a binary search tree.
Dynamic set backed by balanced binary search trees with automatic memory management.
Key * insert(const Key &key)
Inserts a key into the dynamic set.
Arc for graphs implemented with simple adjacency lists.
Graph implemented with double-linked adjacency lists.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Arc Arc
The node class type.
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Graph class implemented with singly-linked adjacency lists.
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)
std::chrono::steady_clock Clock
DynArray< Graph::Node * > nodes
Main namespace for Aleph-w library functions.
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 std::atomic< bool > init
void quicksort_op(C< T > &a, const Compare &cmp=Compare(), const size_t threshold=Quicksort_Threshold)
Optimized quicksort for containers using operator().
static struct argp_option options[]
Default filter for filtered iterators on arcs.
Arc of graph implemented with double-linked adjacency lists.
Result of minimum-cost perfect matching.
Cost_Type total_cost
Total cost if feasible.
Array-based graph implementation.
Dynamic array container with automatic resizing.
Dynamic key-value map based on balanced binary search trees.
Dynamic set implementations based on balanced binary search trees.
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.