1
0
Fork 0

Compare commits

...

10 Commits

4 changed files with 924 additions and 511 deletions

View File

@ -77,3 +77,27 @@ jobs:
- name: Run unittests
run: python3 python/test_mwmatching.py
# Run tests on C++ code.
test-cpp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Boost
run: sudo apt-get install -y libboost-test-dev
- name: Show GCC version
run: g++ --version
- name: Build mwmatching
run: make -C cpp run_matching test_mwmatching
- name: Run unittests
run: cpp/test_mwmatching
- name: Run test graphs
run: >
python3 tests/run_test.py --solver cpp/run_matching --verify
tests/graphs/chain_n1000.edge
tests/graphs/chain_n5000.edge
tests/graphs/sparse_delta_n1004.edge
tests/graphs/triangles_n1002.edge
tests/graphs/triangles_n5001.edge
tests/graphs/random_n1000_m10000.edge
tests/graphs/random_n2000_m10000.edge

View File

@ -36,6 +36,11 @@ This repository is probably not the best place if ...
test_mwmatching.py : Unit tests
run_matching.py : Command-line program to run the matching algorithm
cpp/
mwmatching.h : C++ implementation of maximum weight matching
test_mwmatching.cpp : Unit tests
run_matching.cpp : Command-line program to run the matching algorithm
tests/
generate/ : Python programs to generate graphs
graphs/ : Collection of graphs and matching outputs

File diff suppressed because it is too large Load Diff

View File

@ -363,6 +363,132 @@ BOOST_AUTO_TEST_CASE(test_fail_bad_graph)
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>
@ -497,3 +623,129 @@ BOOST_AUTO_TEST_CASE(test41_maxcard)
}
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()