737 lines
25 KiB
C++
737 lines
25 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.h"
|
|
|
|
|
|
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(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()
|