1
0
Fork 0
maximum-weight-matching/cpp/test_mwmatching.cpp

842 lines
28 KiB
C++

/*
* Unit tests for maximum weight matching.
*
* Depends on the Boost.Test unit test framework.
* Tested with Boost v1.74, available from https://www.boost.org/
*/
#include <algorithm>
#include <limits>
#include <stdexcept>
#include <vector>
#define BOOST_TEST_MODULE mwmatching
#include <boost/test/unit_test.hpp>
#include "mwmatching.hpp"
using EdgeVectorLong = std::vector<mwmatching::Edge<long>>;
using EdgeVectorDouble = std::vector<mwmatching::Edge<double>>;
using Matching = std::vector<mwmatching::VertexPair>;
/* ********** Test maximum_weight_matching() ********** */
BOOST_AUTO_TEST_SUITE(test_maximum_weight_matching)
BOOST_AUTO_TEST_CASE(test10_empty)
{
EdgeVectorLong edges = {};
Matching expect = {};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test11_single_edge)
{
EdgeVectorLong edges = {{0, 1, 1}};
Matching expect = {{0, 1}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test12_simple)
{
EdgeVectorLong edges = {{1, 2, 10}, {2, 3, 11}};
Matching expect = {{2, 3}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test13_simple)
{
EdgeVectorLong edges = {{1, 2, 5}, {2, 3, 11}, {3, 4, 5}};
Matching expect = {{2, 3}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test15_float)
{
EdgeVectorDouble edges = {{1, 2, 3.1415}, {2, 3, 2.7183}, {1, 3, 3.0}, {1, 4, 1.4142}};
Matching expect = {{2, 3}, {1, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test16_negative)
{
EdgeVectorLong edges = {{1, 2, 2}, {1, 3, -2}, {2, 3, 1}, {2, 4, -1}, {3, 4, -6}};
Matching expect = {{1, 2}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test20_sblossom)
{
EdgeVectorLong edges = {{1, 2, 8}, {1, 3, 9}, {2, 3, 10}, {3, 4, 7}};
Matching expect = {{1, 2}, {3, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test20a_sblossom)
{
EdgeVectorLong edges = {{1, 2, 8}, {1, 3, 9}, {2, 3, 10}, {3, 4, 7}, {1, 6, 5}, {4, 5, 6}};
Matching expect = {{2, 3}, {1, 6}, {4, 5}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test21_tblossom)
{
EdgeVectorLong edges = {{1, 2, 9}, {1, 3, 8}, {2, 3, 10}, {1, 4, 5}, {4, 5, 4}, {1, 6, 3}};
Matching expect = {{2, 3}, {4, 5}, {1, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test21a_tblossom)
{
EdgeVectorLong edges = {{1, 2, 9}, {1, 3, 8}, {2, 3, 10}, {1, 4, 5}, {4, 5, 3}, {1, 6, 4}};
Matching expect = {{2, 3}, {4, 5}, {1, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test21b_tblossom)
{
EdgeVectorLong edges = {{1, 2, 9}, {1, 3, 8}, {2, 3, 10}, {1, 4, 5}, {4, 5, 3}, {3, 6, 4}};
Matching expect = {{1, 2}, {4, 5}, {3, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test22_s_nest)
{
EdgeVectorLong edges = {{1, 2, 9}, {1, 3, 9}, {2, 3, 10}, {2, 4, 8}, {3, 5, 8}, {4, 5, 10}, {5, 6, 6}};
Matching expect = {{1, 3}, {2, 4}, {5, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test23_s_relabel_nest)
{
EdgeVectorLong edges = {
{1, 2, 10}, {1, 7, 10}, {2, 3, 12}, {3, 4, 20}, {3, 5, 20}, {4, 5, 25}, {5, 6, 10}, {6, 7, 10}, {7, 8, 8}};
Matching expect = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test24_s_nest_expand)
{
EdgeVectorLong edges = {
{1, 2, 8}, {1, 3, 8}, {2, 3, 10}, {2, 4, 12}, {3, 5, 12}, {4, 5, 14}, {4, 6, 12}, {5, 7, 12}, {6, 7, 14},
{7, 8, 12}};
Matching expect = {{1, 2}, {3, 5}, {4, 6}, {7, 8}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test25_s_t_expand)
{
EdgeVectorLong edges = {
{1, 2, 23}, {1, 5, 22}, {1, 6, 15}, {2, 3, 25}, {3, 4, 22}, {4, 5, 25}, {4, 8, 14}, {5, 7, 13}};
Matching expect = {{1, 6}, {2, 3}, {4, 8}, {5, 7}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test26_s_nest_t_expand)
{
EdgeVectorLong edges = {
{1, 2, 19}, {1, 3, 20}, {1, 8, 8}, {2, 3, 25}, {2, 4, 18}, {3, 5, 18}, {4, 5, 13}, {4, 7, 7}, {5, 6, 7}};
Matching expect = {{1, 8}, {2, 3}, {4, 7}, {5, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test30_tnasty_expand)
{
EdgeVectorLong edges = {
{1, 2, 45}, {1, 5, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 50}, {1, 6, 30}, {3, 9, 35}, {4, 8, 35},
{5, 7, 26}, {9, 10, 5}};
Matching expect = {{2, 3}, {1, 6}, {4, 8}, {5, 7}, {9, 10}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test31_tnasty_expand)
{
EdgeVectorLong edges = {
{1, 2, 45}, {1, 5, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 50}, {1, 6, 30}, {3, 9, 35}, {4, 8, 26},
{5, 7, 40}, {9, 10, 5}};
Matching expect = {{2, 3}, {1, 6}, {4, 8}, {5, 7}, {9, 10}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test32_t_expand_leastslack)
{
EdgeVectorLong edges = {
{1, 2, 45}, {1, 5, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 50}, {1, 6, 30}, {3, 9, 35}, {4, 8, 28},
{5, 7, 26}, {9, 10, 5}};
Matching expect = {{2, 3}, {1, 6}, {4, 8}, {5, 7}, {9, 10}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test33_nest_tnasty_expand)
{
EdgeVectorLong edges = {
{1, 2, 45}, {1, 7, 45}, {2, 3, 50}, {3, 4, 45}, {4, 5, 95}, {4, 6, 94}, {5, 6, 94}, {6, 7, 50},
{1, 8, 30}, {3, 11, 35}, {5, 9, 36}, {7, 10, 26}, {11, 12, 5}};
Matching expect = {{2, 3}, {4, 6}, {1, 8}, {5, 9}, {7, 10}, {11, 12}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test34_nest_relabel_expand)
{
EdgeVectorLong edges = {
{1, 2, 40}, {1, 3, 40}, {2, 3, 60}, {2, 4, 55}, {3, 5, 55}, {4, 5, 50}, {1, 8, 15}, {5, 7, 30},
{7, 6, 10}, {8, 10, 10}, {4, 9, 30}};
Matching expect = {{1, 2}, {3, 5}, {7, 6}, {8, 10}, {4, 9}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test41_nonmax_card)
{
EdgeVectorLong edges = {{0, 1, 2}, {0, 4, 3}, {1, 2, 7}, {1, 5, 2}, {2, 3, 9}, {2, 5, 4}, {3, 4, 8}, {3, 5, 4}};
Matching expect = {{1, 2}, {3, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test42_s_nest_partial_expand)
{
/*
* [0]--8--[1]--6--[3]--5--[5]
* \ | |
* \ 9 8
* 8 | |
* \--[2]--7--[4]
*/
EdgeVectorLong edges = {{0, 1, 8}, {0, 2, 8}, {1, 2, 9}, {1, 3, 6}, {2, 4, 7}, {3, 4, 8}, {3, 5, 5}};
Matching expect = {{0, 1}, {2, 4}, {3, 5}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test43_s_nest_noexpand)
{
/*
* [1]--9--[2]
* | /
* 7 ___7
* | /
* [0] [5]--2--[6]
* | \___
* 7 7
* | \
* [3]--9--[4]
*/
EdgeVectorLong edges = {{0, 1, 7}, {0, 2, 7}, {1, 2, 9}, {0, 3, 7}, {0, 4, 7}, {3, 4, 9}, {5, 6, 2}};
Matching expect = {{1, 2}, {3, 4}, {5, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test44_blossom_redundant_edge)
{
/*
* [1]----9---[2]
* / | \
* 7 8 \
* / | 1
* [0]--6--[4]--9--[3] |
* \ |
* \----1----[5]
*/
EdgeVectorLong edges = {{0, 1, 7}, {0, 4, 6}, {1, 2, 9}, {2, 3, 8}, {3, 4, 9}, {3, 5, 1}, {4, 5, 1}};
Matching expect = {{1, 2}, {3, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test45_flip_order)
{
EdgeVectorLong edges = {
{0, 1, 2}, {0, 4, 3}, {1, 2, 7}, {1, 5, 1}, {2, 3, 9}, {2, 5, 4}, {3, 4, 8}, {3, 5, 4}};
Matching expect = {{1, 2}, {3, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
EdgeVectorLong redges(edges.rbegin(), edges.rend());
Matching rexpect(expect.rbegin(), expect.rend());
BOOST_TEST(mwmatching::maximum_weight_matching(redges) == rexpect);
for (mwmatching::Edge<long>& edge : edges) {
std::swap(edge.vt.first, edge.vt.second);
}
for (mwmatching::VertexPair& vt : expect) {
std::swap(vt.first, vt.second);
}
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
edges = {{0, 2, 4}, {0, 3, 4}, {0, 4, 1}, {1, 2, 8}, {1, 5, 3}, {2, 3, 9}, {3, 4, 7}, {4, 5, 2}};
expect = {{1, 2}, {3, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test46_expand_unlabeled_blossom)
{
/*
* 3--[1]
* / |
* [2]--1--[4] 7
* \ |
* 5--[3]--5--[5]
*/
EdgeVectorLong edges = {{1, 3, 7}, {1, 4, 3}, {2, 4, 1}, {3, 4, 5}, {3, 5, 5}};
Matching expect1 = {{1, 3}, {2, 4}};
Matching expect2 = {{1, 4}, {3, 5}};
Matching result = mwmatching::maximum_weight_matching(edges);
BOOST_TEST((result == expect1 || result == expect2));
}
BOOST_AUTO_TEST_CASE(test47_expand_unlabeled_outer)
{
/*
* [3]--10--[1]--15--[2]--12--[5]
* _/ \_ | |
* 11 16_ 8 15
* / \ | |
* [4] [6]---7--[7]
*/
EdgeVectorLong edges = {
{1, 2, 15}, {1, 3, 10}, {1, 4, 11}, {1, 6, 17}, {2, 5, 12}, {2, 6, 8}, {5, 7, 15}, {6, 7, 7}};
Matching expect = {{1, 4}, {2, 6}, {5, 7}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test48_expand_unlabeled_nested)
{
/*
* [5]--11--[1]--11 14--[3]
* | \ / |
* 12 [4] 14
* | / \ |
* [6]--11--[2]--12 11--[7]
*/
EdgeVectorLong edges = {
{1, 2, 12}, {1, 4, 11}, {1, 5, 11}, {2, 4, 12}, {2, 6, 11}, {3, 4, 14}, {3, 7, 14}, {4, 7, 11}};
Matching expect = {{1, 5}, {2, 4}, {3, 7}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test51_augment_blossom_nested)
{
/*
* 19--[1]--17--[4]--11--[7]
* / | |
* [3]--15--[2] 19 18
* \ | |
* 21--[5]--19--[6]
*/
EdgeVectorLong edges = {
{1, 2, 19}, {1, 4, 17}, {1, 5, 19}, {2, 3, 15}, {2, 5, 21}, {4, 6, 18}, {4, 7, 11}, {5, 6, 19}};
Matching expect = {{1, 5}, {2, 3}, {4, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test52_augment_blossom_nested2)
{
/*
*
* [4]--15 19--[2]
* | \ / |
* 16 [1] 21
* | / \ |
* [5]--17 19--[3]
* |
* 10
* |
* [6]
*/
EdgeVectorLong edges = {
{1, 2, 19}, {1, 3, 19}, {1, 4, 15}, {1, 5, 17}, {2, 3, 21}, {4, 5, 16}, {5, 6, 10}};
Matching expect = {{1, 4}, {2, 3}, {5, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test61_triangles_n9)
{
/*
* t.f 9 nodes
*
* [1]------[4] [7]
* | \ | \ | \
* | [3] | [6] | [9]
* | / | / | /
* [2] [5]------[8]
*/
EdgeVectorLong edges = {
{1, 2, 1}, {1, 3, 1}, {2, 3, 1}, {4, 5, 1}, {4, 6, 1}, {5, 6, 1}, {7, 8, 1}, {7, 9, 1}, {8, 9, 1}, {1, 4, 1}, {5, 8, 1}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges).size() == 4);
}
BOOST_AUTO_TEST_CASE(test_fail_bad_input)
{
EdgeVectorDouble inf_weight = {{1, 2, std::numeric_limits<double>::infinity()}};
BOOST_CHECK_THROW(mwmatching::maximum_weight_matching(inf_weight), std::invalid_argument);
EdgeVectorDouble huge_weight = {{1, 2, std::numeric_limits<double>::max() / 2}};
BOOST_CHECK_THROW(mwmatching::maximum_weight_matching(huge_weight), std::invalid_argument);
}
BOOST_AUTO_TEST_CASE(test_fail_bad_graph)
{
EdgeVectorLong self_edge = {{0, 1, 2}, {1, 1, 1}};
BOOST_CHECK_THROW(mwmatching::maximum_weight_matching(self_edge), std::invalid_argument);
EdgeVectorLong duplicate_edge = {{0, 1, 2}, {1, 2, 1}, {2, 1, 1}};
BOOST_CHECK_THROW(mwmatching::maximum_weight_matching(duplicate_edge), std::invalid_argument);
}
BOOST_AUTO_TEST_SUITE_END()
/* ********** Test corner cases of maximum_weight_matching() ********** */
BOOST_AUTO_TEST_SUITE(test_corner_cases)
BOOST_AUTO_TEST_CASE(test1)
{
EdgeVectorLong edges = {{0, 4, 26}, {1, 3, 31}, {1, 4, 49}};
Matching expect = {{0, 4}, {1, 3}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test2)
{
EdgeVectorLong edges = {{0, 2, 42}, {0, 4, 36}, {2, 3, 26}};
Matching expect = {{0, 4}, {2, 3}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test3)
{
EdgeVectorLong edges = {{0, 4, 43}, {1, 4, 28}, {2, 4, 38}};
Matching expect = {{0, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test4)
{
EdgeVectorLong edges = {{0, 1, 50}, {0, 3, 46}, {0, 4, 45}};
Matching expect = {{0, 1}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test5)
{
EdgeVectorLong edges = {{0, 1, 35}, {0, 3, 36}, {0, 4, 46}};
Matching expect = {{0, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test6)
{
EdgeVectorLong edges = {{0, 1, 50}, {0, 4, 51}, {0, 5, 34}, {1, 2, 43}, {1, 4, 57}, {2, 5, 47}, {3, 4, 17}};
Matching expect = {{0, 1}, {2, 5}, {3, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test7)
{
EdgeVectorLong edges = {{0, 1, 34}, {0, 3, 19}, {1, 2, 45}, {1, 3, 30}, {1, 4, 37}, {2, 4, 36}};
Matching expect = {{0, 1}, {2, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test8)
{
EdgeVectorLong edges = {{0, 1, 48}, {0, 3, 42}, {0, 4, 57}, {1, 3, 51}, {1, 5, 36}, {2, 3, 23}, {4, 5, 46}};
Matching expect = {{0, 1}, {2, 3}, {4, 5}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test9)
{
EdgeVectorLong edges = {{0, 1, 21}, {0, 2, 25}, {1, 4, 40}, {2, 3, 10}, {2, 5, 40}, {3, 5, 31}, {4, 5, 58}};
Matching expect = {{0, 2}, {1, 4}, {3, 5}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test10)
{
EdgeVectorLong edges = {{0, 2, 7}, {0, 5, 20}, {1, 2, 50}, {1, 4, 46}, {2, 3, 35}, {2, 4, 8}, {2, 5, 25}, {3, 5, 47}};
Matching expect = {{0, 5}, {1, 4}, {2, 3}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test11)
{
EdgeVectorLong edges = {{0, 1, 42}, {0, 2, 60}, {1, 3, 34}, {1, 4, 58}, {1, 5, 52}, {2, 5, 60}, {3, 5, 34}, {4, 5, 57}};
Matching expect = {{0, 2}, {1, 4}, {3, 5}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test12)
{
EdgeVectorLong edges = {{0, 1, 23}, {0, 2, 26}, {0, 3, 2}, {0, 4, 41}, {2, 4, 36}};
Matching expect = {{0, 1}, {2, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test13)
{
EdgeVectorLong edges = {{0, 3, 58}, {0, 4, 49}, {1, 5, 34}, {2, 3, 22}, {2, 5, 42}, {4, 5, 36}};
Matching expect = {{0, 4}, {1, 5}, {2, 3}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test14)
{
EdgeVectorLong edges = {{0, 1, 29}, {0, 3, 35}, {0, 4, 42}, {1, 2, 12}, {2, 4, 29}, {3, 4, 44}};
Matching expect = {{0, 1}, {3, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test15)
{
EdgeVectorLong edges = {{0, 4, 53}, {0, 5, 42}, {1, 4, 45}, {2, 4, 59}, {2, 6, 39}, {4, 5, 69}, {4, 6, 52}};
Matching expect = {{0, 5}, {1, 4}, {2, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test16)
{
EdgeVectorLong edges = {{0, 2, 13}, {1, 2, 11}, {2, 3, 39}, {2, 4, 17}, {3, 4, 35}};
Matching expect = {{0, 2}, {3, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test17)
{
EdgeVectorLong edges = {{0, 1, 48}, {0, 2, 44}, {0, 4, 48}, {1, 4, 36}, {3, 4, 31}};
Matching expect = {{0, 2}, {1, 4}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_SUITE_END()
/* ********** Test adjust_weights_for_maximum_cardinality_matching() ********** */
template <typename WeightType>
static bool equal_weight(WeightType lhs, WeightType rhs);
template <>
bool equal_weight<long>(long lhs, long rhs)
{
return (lhs == rhs);
}
template <>
bool equal_weight<double>(double lhs, double rhs)
{
static const boost::math::fpc::small_with_tolerance<double> eq(1e-6);
return eq(lhs - rhs);
}
template <typename WeightType>
static bool equal_edges(const std::vector<mwmatching::Edge<WeightType>>& lhs,
const std::vector<mwmatching::Edge<WeightType>>& rhs)
{
if (lhs.size() != rhs.size()) {
return false;
}
auto li = lhs.begin();
auto ri = rhs.begin();
while ((li != lhs.end()) && (ri != rhs.end())) {
if (li->vt != ri->vt) {
return false;
}
if (! equal_weight(li->weight, ri->weight)) {
return false;
}
++li;
++ri;
}
return (li == lhs.end()) && (ri == rhs.end());
}
BOOST_AUTO_TEST_SUITE(test_adjust_weights_for_maximum_cardinality)
BOOST_AUTO_TEST_CASE(test_empty)
{
EdgeVectorLong edges = {};
EdgeVectorLong expect = {};
BOOST_TEST(equal_edges(mwmatching::adjust_weights_for_maximum_cardinality_matching(edges), expect));
}
BOOST_AUTO_TEST_CASE(test_chain)
{
EdgeVectorLong edges = {{0, 1, 2}, {1, 2, 8}, {2, 3, 3}, {3, 4, 9}, {4, 5, 1}, {5, 6, 7}, {6, 7, 4}};
EdgeVectorLong expect = {{0, 1, 65}, {1, 2, 71}, {2, 3, 66}, {3, 4, 72}, {4, 5, 64}, {5, 6, 70}, {6, 7, 67}};
BOOST_TEST(equal_edges(mwmatching::adjust_weights_for_maximum_cardinality_matching(edges), expect));
}
BOOST_AUTO_TEST_CASE(test_chain_preadjusted)
{
EdgeVectorLong edges = {{0, 1, 65}, {1, 2, 71}, {2, 3, 66}, {3, 4, 72}, {4, 5, 64}, {5, 6, 70}, {6, 7, 67}};
BOOST_TEST(equal_edges(mwmatching::adjust_weights_for_maximum_cardinality_matching(edges), edges));
}
BOOST_AUTO_TEST_CASE(test_flat)
{
EdgeVectorLong edges = {{0, 1, 0}, {0, 4, 0}, {1, 2, 0}, {1, 5, 0}, {2, 3, 0}, {2, 5, 0}, {3, 4, 0}, {3, 5, 0}};
EdgeVectorLong expect = {{0, 1, 1}, {0, 4, 1}, {1, 2, 1}, {1, 5, 1}, {2, 3, 1}, {2, 5, 1}, {3, 4, 1}, {3, 5, 1}};
BOOST_TEST(equal_edges(mwmatching::adjust_weights_for_maximum_cardinality_matching(edges), expect));
}
BOOST_AUTO_TEST_CASE(test_simple)
{
EdgeVectorLong edges = {{1, 2, 5}, {2, 3, 11}, {3, 4, 5}};
EdgeVectorLong expect = {{1, 2, 30}, {2, 3, 36}, {3, 4, 30}};
BOOST_TEST(equal_edges(mwmatching::adjust_weights_for_maximum_cardinality_matching(edges), expect));
}
BOOST_AUTO_TEST_CASE(test_negative)
{
EdgeVectorLong edges = {{1, 2, 2}, {1, 3, -2}, {2, 3, 1}, {2, 4, -1}, {3, 4, -6}};
EdgeVectorLong expect = {{1, 2, 48}, {1, 3, 44}, {2, 3, 47}, {2, 4, 45}, {3, 4, 40}};
BOOST_TEST(equal_edges(mwmatching::adjust_weights_for_maximum_cardinality_matching(edges), expect));
}
BOOST_AUTO_TEST_CASE(test_float)
{
EdgeVectorDouble edges = {{0, 1, 1}, {1, 2, 1.4142}, {2, 3, 1.6180}, {3, 4, 2.7182}, {4, 5, 3.1415}};
EdgeVectorDouble expect = {{0, 1, 12.8490}, {1, 2, 13.2632}, {2, 3, 13.4670}, {3, 4, 14.5672}, {4, 5, 14.9905}};
BOOST_TEST(equal_edges(mwmatching::adjust_weights_for_maximum_cardinality_matching(edges), expect));
}
BOOST_AUTO_TEST_CASE(test_fail_bad_range)
{
const long max_long = std::numeric_limits<long>::max();
EdgeVectorLong edges = {{0, 1, -(max_long / 8)}, {1, 2, 0}, {2, 3, max_long / 8}};
BOOST_CHECK_THROW(mwmatching::adjust_weights_for_maximum_cardinality_matching(edges), std::invalid_argument);
}
BOOST_AUTO_TEST_SUITE_END()
/* ********** Test maximum cardinality matching ********** */
BOOST_AUTO_TEST_SUITE(test_maximum_cardinality_matching)
BOOST_AUTO_TEST_CASE(test14_maxcard)
{
EdgeVectorLong edges = {{1, 2, 5}, {2, 3, 11}, {3, 4, 5}};
Matching expect = {{1, 2}, {3, 4}};
BOOST_TEST(
mwmatching::maximum_weight_matching(
mwmatching::adjust_weights_for_maximum_cardinality_matching(edges)) == expect);
}
BOOST_AUTO_TEST_CASE(test16_negative)
{
EdgeVectorLong edges = {{1, 2, 2}, {1, 3, -2}, {2, 3, 1}, {2, 4, -1}, {3, 4, -6}};
Matching expect = {{1, 3}, {2, 4}};
BOOST_TEST(
mwmatching::maximum_weight_matching(
mwmatching::adjust_weights_for_maximum_cardinality_matching(edges)) == expect);
}
BOOST_AUTO_TEST_CASE(test41_maxcard)
{
EdgeVectorLong edges = {{0, 1, 2}, {0, 4, 3}, {1, 2, 7}, {1, 5, 2}, {2, 3, 9}, {2, 5, 4}, {3, 4, 8}, {3, 5, 4}};
Matching expect1 = {{0, 1}, {2, 5}, {3, 4}};
Matching expect2 = {{0, 4}, {1, 2}, {3, 5}};
Matching result =
mwmatching::maximum_weight_matching(
mwmatching::adjust_weights_for_maximum_cardinality_matching(edges));
BOOST_TEST((result == expect1 || result == expect2));
}
BOOST_AUTO_TEST_SUITE_END()
/* ********** Test verification ********** */
BOOST_AUTO_TEST_SUITE(test_verification)
BOOST_AUTO_TEST_CASE(test_success)
{
using mwmatching::impl::NO_VERTEX;
EdgeVectorLong edges = {{0, 1, 10}, {1, 2, 11}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {NO_VERTEX, 2, 1};
ctx.vertex_dual = {0, 20, 2};
ctx.nontrivial_blossom.clear();
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == true);
}
BOOST_AUTO_TEST_CASE(test_asymmetric_matching)
{
using mwmatching::impl::NO_VERTEX;
EdgeVectorLong edges = {{0, 1, 10}, {1, 2, 11}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {NO_VERTEX, 2, 0};
ctx.vertex_dual = {0, 20, 2};
ctx.nontrivial_blossom.clear();
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == false);
}
BOOST_AUTO_TEST_CASE(test_nonexistent_matched_edge)
{
using mwmatching::impl::NO_VERTEX;
EdgeVectorLong edges = {{0, 1, 10}, {1, 2, 11}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {2, NO_VERTEX, 0};
ctx.vertex_dual = {11, 11, 11};
ctx.nontrivial_blossom.clear();
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == false);
}
BOOST_AUTO_TEST_CASE(test_negative_vertex_dual)
{
using mwmatching::impl::NO_VERTEX;
EdgeVectorLong edges = {{0, 1, 10}, {1, 2, 11}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {NO_VERTEX, 2, 1};
ctx.vertex_dual = {-2, 22, 0};
ctx.nontrivial_blossom.clear();
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == false);
}
BOOST_AUTO_TEST_CASE(test_unmatched_nonzero_dual)
{
using mwmatching::impl::NO_VERTEX;
EdgeVectorLong edges = {{0, 1, 10}, {1, 2, 11}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {NO_VERTEX, 2, 1};
ctx.vertex_dual = {9, 11, 11};
ctx.nontrivial_blossom.clear();
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == false);
}
BOOST_AUTO_TEST_CASE(test_negative_edge_slack)
{
using mwmatching::impl::NO_VERTEX;
EdgeVectorLong edges = {{0, 1, 10}, {1, 2, 11}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {NO_VERTEX, 2, 1};
ctx.vertex_dual = {0, 11, 11};
ctx.nontrivial_blossom.clear();
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == false);
}
BOOST_AUTO_TEST_CASE(test_matched_edge_slack)
{
using mwmatching::impl::NO_VERTEX;
EdgeVectorLong edges = {{0, 1, 10}, {1, 2, 11}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {NO_VERTEX, 2, 1};
ctx.vertex_dual = {0, 20, 11};
ctx.nontrivial_blossom.clear();
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == false);
}
BOOST_AUTO_TEST_CASE(test_negative_blossom_dual)
{
EdgeVectorLong edges = {{0, 1, 7}, {0, 2, 8}, {1, 2, 9}, {2, 3, 6}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {1, 0, 3, 2};
ctx.vertex_dual = {4, 6, 8, 4};
mwmatching::impl::Blossom<long> b0(0);
mwmatching::impl::Blossom<long> b1(1);
mwmatching::impl::Blossom<long> b2(2);
ctx.nontrivial_blossom.emplace_back(
std::vector<mwmatching::impl::Blossom<long>*>{&b0, &b1, &b2},
std::deque<mwmatching::VertexPair>{{0, 1}, {1, 2}, {2, 0}});
ctx.nontrivial_blossom.front().dual_var = -2;
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == false);
}
BOOST_AUTO_TEST_CASE(test_blossom_not_full)
{
using mwmatching::impl::NO_VERTEX;
EdgeVectorLong edges = {{0, 1, 7}, {0, 2, 2}, {1, 2, 5}, {0, 3, 8}, {1, 4, 8}};
mwmatching::impl::MatchingContext<long> ctx(edges);
ctx.vertex_mate = {3, 4, NO_VERTEX, 0, 1};
ctx.vertex_dual = {4, 10, 0, 12, 6};
mwmatching::impl::Blossom<long> b0(0);
mwmatching::impl::Blossom<long> b1(1);
mwmatching::impl::Blossom<long> b2(2);
ctx.nontrivial_blossom.emplace_back(
std::vector<mwmatching::impl::Blossom<long>*>{&b0, &b1, &b2},
std::deque<mwmatching::VertexPair>{{0, 1}, {1, 2}, {2, 0}});
ctx.nontrivial_blossom.front().dual_var = 2;
mwmatching::impl::MatchingVerifier<long> verifier(ctx);
BOOST_TEST(verifier.verify() == false);
}
BOOST_AUTO_TEST_SUITE_END()
/* ********** Test graphs that force big values for dual / slack ********** */
BOOST_AUTO_TEST_SUITE(test_value_range)
BOOST_AUTO_TEST_CASE(test_big_blossom_dual)
{
/*
* Force modified blossom dual close to 2*maxweight.
*
* [3]
* / \
* W-4/ \W
* 7 / \
* [1]-----[2]-----[4]
* | W-4
* 5|
* | 1
* [5]-----[6]
*/
long w = std::numeric_limits<long>::max() / 6;
EdgeVectorLong edges = {{1, 2, 7}, {2, 3, w - 4}, {2, 4, w - 4}, {2, 5, 5}, {3, 4, w}, {5, 6, 1}};
Matching expect = {{1, 2}, {3, 4}, {5, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test_negative_blossom_dual)
{
/*
* Force modified blossom dual close to -maxweight.
*
* [3]
* / \
* 5/ \7
* 1 / \
* [1]-----[2]-----[4]
* | 5
* 1|
* | W
* [5]-----[6]
*/
long w = std::numeric_limits<long>::max() / 6;
EdgeVectorLong edges = {{1, 2, 1}, {2, 3, 5}, {2, 4, 5}, {2, 5, 1}, {3, 4, 7}, {5, 6, w}};
Matching expect = {{1, 2}, {3, 4}, {5, 6}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_CASE(test_big_edge_slack)
{
/*
* Force modified edge slack close to 3*maxweight.
*
* 6 W W-2 5
* [1]-----[2]-----[3]-----[4]-----[5]
* | |
* |1 |1
* | |
* [6]-----[7]-----[8]-----[9]-----[10]
* 6 W W-2 5
*/
long w = std::numeric_limits<long>::max() / 6;
EdgeVectorLong edges = {{1, 2, 6}, {1, 6, 1}, {2, 3, w}, {3, 4, w - 2}, {3, 8, 1}, {4, 5, 5},
{6, 7, 6}, {7, 8, w}, {8, 9, w - 2}, {9, 10, 5}};
Matching expect = {{1, 6}, {2, 3}, {4, 5}, {7, 8}, {9, 10}};
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
}
BOOST_AUTO_TEST_SUITE_END()