1
0
Fork 0

Add C++ tests for maximum cardinality matching

This commit is contained in:
Joris van Rantwijk 2023-05-12 18:11:02 +02:00
parent d08e3e1c58
commit 8d7d1a537a
1 changed files with 151 additions and 66 deletions

View File

@ -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."""