Add C++ tests for maximum cardinality matching
This commit is contained in:
parent
d08e3e1c58
commit
8d7d1a537a
|
@ -21,6 +21,8 @@ using EdgeVectorDouble = std::vector<mwmatching::Edge<double>>;
|
||||||
using Matching = std::vector<mwmatching::VertexPair>;
|
using Matching = std::vector<mwmatching::VertexPair>;
|
||||||
|
|
||||||
|
|
||||||
|
/* ********** Test maximum_weight_matching() ********** */
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE(test_maximum_weight_matching)
|
BOOST_AUTO_TEST_SUITE(test_maximum_weight_matching)
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(test10_empty)
|
BOOST_AUTO_TEST_CASE(test10_empty)
|
||||||
|
@ -325,6 +327,21 @@ BOOST_AUTO_TEST_CASE(test51_augment_blossom_nested)
|
||||||
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
|
BOOST_TEST(mwmatching::maximum_weight_matching(edges) == expect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test61_unsigned)
|
||||||
|
{
|
||||||
|
std::vector<mwmatching::Edge<unsigned int>> 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(test62_unsigned2)
|
||||||
|
{
|
||||||
|
std::vector<mwmatching::Edge<unsigned int>> 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(test_fail_bad_input)
|
BOOST_AUTO_TEST_CASE(test_fail_bad_input)
|
||||||
{
|
{
|
||||||
EdgeVectorDouble inf_weight = {{1, 2, std::numeric_limits<double>::infinity()}};
|
EdgeVectorDouble inf_weight = {{1, 2, std::numeric_limits<double>::infinity()}};
|
||||||
|
@ -343,13 +360,146 @@ BOOST_AUTO_TEST_CASE(test_fail_bad_graph)
|
||||||
BOOST_CHECK_THROW(mwmatching::maximum_weight_matching(duplicate_edge), std::invalid_argument);
|
BOOST_CHECK_THROW(mwmatching::maximum_weight_matching(duplicate_edge), std::invalid_argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
// TODO : test with unsigned weight type
|
|
||||||
|
|
||||||
|
/* ********** 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()
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
class TestCornerCases(unittest.TestCase):
|
class TestCornerCases(unittest.TestCase):
|
||||||
|
@ -428,71 +578,6 @@ class TestCornerCases(unittest.TestCase):
|
||||||
self.assertEqual(pairs, [(0,2), (1,4)])
|
self.assertEqual(pairs, [(0,2), (1,4)])
|
||||||
|
|
||||||
|
|
||||||
class TestAdjustWeightForMaxCardinality(unittest.TestCase):
|
|
||||||
"""Test adjust_weights_for_maximum_cardinality_matching() function."""
|
|
||||||
|
|
||||||
def test_empty(self):
|
|
||||||
self.assertEqual(adj([]), [])
|
|
||||||
|
|
||||||
def test_chain(self):
|
|
||||||
self.assertEqual(
|
|
||||||
adj([(0,1,2), (1,2,8), (2,3,3), (3,4,9), (4,5,1), (5,6,7), (6,7,4)]),
|
|
||||||
[(0,1,65), (1,2,71), (2,3,66), (3,4,72), (4,5,64), (5,6,70), (6,7,67)])
|
|
||||||
|
|
||||||
def test_chain_preadjusted(self):
|
|
||||||
self.assertEqual(
|
|
||||||
adj([(0,1,65), (1,2,71), (2,3,66), (3,4,72), (4,5,64), (5,6,70), (6,7,67)]),
|
|
||||||
[(0,1,65), (1,2,71), (2,3,66), (3,4,72), (4,5,64), (5,6,70), (6,7,67)])
|
|
||||||
|
|
||||||
def test_flat(self):
|
|
||||||
self.assertEqual(
|
|
||||||
adj([(0,1,0), (0,4,0), (1,2,0), (1,5,0), (2,3,0), (2,5,0), (3,4,0), (3,5,0)]),
|
|
||||||
[(0,1,1), (0,4,1), (1,2,1), (1,5,1), (2,3,1), (2,5,1), (3,4,1), (3,5,1)])
|
|
||||||
|
|
||||||
def test14_maxcard(self):
|
|
||||||
self.assertEqual(
|
|
||||||
adj([(1,2,5), (2,3,11), (3,4,5)]),
|
|
||||||
[(1,2,30), (2,3,36), (3,4,30)])
|
|
||||||
|
|
||||||
def test16_negative(self):
|
|
||||||
self.assertEqual(
|
|
||||||
adj([(1,2,2), (1,3,-2), (2,3,1), (2,4,-1), (3,4,-6)]),
|
|
||||||
[(1,2,48), (1,3,44), (2,3,47), (2,4,45), (3,4,40)])
|
|
||||||
|
|
||||||
|
|
||||||
class TestMaximumCardinalityMatching(unittest.TestCase):
|
|
||||||
"""Test maximum cardinality matching."""
|
|
||||||
|
|
||||||
def test14_maxcard(self):
|
|
||||||
"""maximum cardinality"""
|
|
||||||
self.assertEqual(
|
|
||||||
mwm(adj([(1,2,5), (2,3,11), (3,4,5)])),
|
|
||||||
[(1,2), (3,4)])
|
|
||||||
|
|
||||||
def test16_negative(self):
|
|
||||||
"""negative weights"""
|
|
||||||
self.assertEqual(
|
|
||||||
mwm(adj([(1,2,2), (1,3,-2), (2,3,1), (2,4,-1), (3,4,-6)])),
|
|
||||||
[(1,3), (2,4)])
|
|
||||||
|
|
||||||
def test43_maxcard(self):
|
|
||||||
"""maximum cardinality"""
|
|
||||||
self.assertIn(
|
|
||||||
mwm(adj([(0,1,2), (0,4,3), (1,2,7), (1,5,2), (2,3,9), (2,5,4), (3,4,8), (3,5,4)])),
|
|
||||||
([(0,1), (2,5), (3,4)],
|
|
||||||
[(0,4), (1,2), (3,5)]))
|
|
||||||
|
|
||||||
|
|
||||||
class TestGraphInfo(unittest.TestCase):
|
|
||||||
"""Test _GraphInfo helper class."""
|
|
||||||
|
|
||||||
def test_empty(self):
|
|
||||||
graph = mwmatching._GraphInfo([])
|
|
||||||
self.assertEqual(graph.num_vertex, 0)
|
|
||||||
self.assertEqual(graph.edges, [])
|
|
||||||
self.assertEqual(graph.adjacent_edges, [])
|
|
||||||
|
|
||||||
|
|
||||||
class TestVerificationFail(unittest.TestCase):
|
class TestVerificationFail(unittest.TestCase):
|
||||||
"""Test failure handling in verification routine."""
|
"""Test failure handling in verification routine."""
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue