Blossom VI is a C++ implementation of a minimum weight perfect matching solver. Given an undirected weighted graph, the solver finds a perfect matching of minimum total edge weight. If the graph does not contain a perfect matching, an exception is thrown.
If you use this code for academic purposes, please cite the paper https://arxiv.org/abs/2604.20351.
Build:
cmake -S . -B cmake-build-release -DCMAKE_BUILD_TYPE=Release
cmake --build cmake-build-release -j2The executable is written to cmake-build-release/blossom_vi.
Requirements:
- CMake 3.20 or newer
- A C++20 compiler
- Boost headers
Run the solver on a graph file:
./cmake-build-release/blossom_vi examples/example.txtOutput:
Optimal weight: 7
To save the matching to a file matching.txt:
./cmake-build-release/blossom_vi examples/example.txt matching.txtThe matching file contains one matched edge per line:
0 2
1 3
By default, stdout contains only the optimal matching weight. To also print the matching:
./cmake-build-release/blossom_vi --print-matching examples/example.txtTo also print the time spent in the main solver function call:
./cmake-build-release/blossom_vi --print-runtime examples/example.txtGraph instances use the following format.
The first line contains:
n m
where n is the number of vertices and m is the number of edges.
The next m lines contain one undirected weighted edge per line:
endpoint1 endpoint2 weight
Vertices are numbered from 0 to n - 1. Weights are integers and may be negative. The absolute values of the edge weights must not exceed
Example:
4 5
0 1 10
0 2 3
1 3 4
2 3 7
1 2 8
The public solver class is MWPMSolver. Construct it from a vector of (endpoint1, endpoint2, weight) tuples, run FindMinPerfectMatching(), then read the matching:
#include <tuple>
#include <utility>
#include <vector>
#include "MWPMSolver.h"
int main() {
std::vector<std::tuple<int, int, int>> edges = {
{0, 1, 10},
{0, 2, 3},
{1, 3, 4},
{2, 3, 7},
{1, 2, 8},
};
MWPMSolver solver(edges);
solver.FindMinPerfectMatching();
const std::vector<std::pair<int, int>>& matching = solver.Matching();
const int64_t optimal_weight = solver.primal_objective;
}There is a correctness test that runs randomized instances. The correctness of the output is validated through verification of the primal feasibility, the dual feasibility, and the complementary slackness conditions.
./cmake-build-release/blossom_vi_testYou can also choose the random graph size, number of iterations, and seed:
./cmake-build-release/blossom_vi_test 100 500 1000 67The arguments are num_vertices, num_edges, iterations, and seed.
num_vertices must be even, and num_edges must be at least
num_vertices / 2.