Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
blossom_example.cc
See also
tpl_bipartite.H For specialized bipartite matching algorithms.
Hungarian.H For minimum/maximum weight assignment matching.
Author
Leandro Rabindranath Leon
/*
Aleph_w
Data structures & Algorithms
version 2.0.0b
https://github.com/lrleon/Aleph-w
This file is part of Aleph-w library
Copyright (c) 2002-2026 Leandro Rabindranath Leon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
# include <algorithm>
# include <fstream>
# include <iomanip>
# include <iostream>
# include <string>
# include <utility>
# include <tpl_array.H>
# include <tpl_dynSetTree.H>
# include <Blossom.H>
# include <tpl_graph.H>
# include <tpl_sgraph.H>
# include <tpl_agraph.H>
using namespace std;
using namespace Aleph;
namespace
{
struct Node_Pos
{
double x = 0;
double y = 0;
string label;
};
struct Scenario
{
string slug;
string title;
};
template <class GT>
GT build_graph(const Scenario & s)
{
GT g;
nodes.reserve(s.nodes.size());
for (size_t i = 0; i < s.nodes.size(); ++i)
nodes.append(g.insert_node(static_cast<int>(i)));
for (const auto & [u, v] : s.edges)
{
ah_range_error_if(u >= s.nodes.size() or v >= s.nodes.size())
<< "Edge endpoint out of range";
g.insert_arc(nodes[u], nodes[v], 1);
}
return g;
}
template <class GT>
Array<pair<size_t, size_t>> extract_matching_pairs(
const GT & g,
const DynDlist<typename GT::Arc *> & matching)
{
for (auto it = matching.get_it(); it.has_curr(); it.next_ne())
{
auto * arc = it.get_curr();
size_t u = static_cast<size_t>(g.get_src_node(arc)->get_info());
size_t v = static_cast<size_t>(g.get_tgt_node(arc)->get_info());
if (u > v)
swap(u, v);
seen.insert(make_pair(u, v));
}
for (const auto & p : seen)
result.append(p);
return result;
}
string format_pairs(const Array<pair<size_t, size_t>> & pairs)
{
if (pairs.is_empty())
return "(empty)";
string out;
for (size_t i = 0; i < pairs.size(); ++i)
{
const auto & [u, v] = pairs[i];
out += "(" + to_string(u) + "," + to_string(v) + ")";
if (i + 1 < pairs.size())
out += " ";
}
return out;
}
void write_tikz(const Scenario & s,
const Array<pair<size_t, size_t>> & matching_pairs,
const string & file_path)
{
for (auto [u, v] : matching_pairs)
{
if (u > v)
swap(u, v);
matched_set.insert(make_pair(u, v));
}
ofstream out(file_path);
if (not out)
{
cerr << "Cannot write file: " << file_path << endl;
return;
}
out << "\\documentclass[tikz,border=10pt]{standalone}\n";
out << "\\usepackage{tikz}\n";
out << "\\usetikzlibrary{arrows.meta}\n";
out << "\\begin{document}\n";
out << "\\begin{tikzpicture}[x=1cm,y=1cm]\n";
out << " \\fill[blue!2] (-4.2,-3.2) rectangle (4.2,3.2);\n";
out << " \\node[font=\\bfseries\\large] at (0,2.95) {"
<< s.title << "};\n";
out << " \\tikzset{"
<< "edge/.style={line width=0.9pt, draw=black!65},"
<< "match/.style={line width=2.6pt, draw=orange!90!black},"
<< "vertex/.style={circle, draw=black!70, fill=white, minimum size=9mm, inner sep=1pt, font=\\small\\bfseries}"
<< "}\n";
for (size_t i = 0; i < s.nodes.size(); ++i)
{
const auto & node = s.nodes[i];
out << " \\node[vertex] (n" << i << ") at ("
<< fixed << setprecision(2) << node.x << "," << node.y << ") {"
<< node.label << "};\n";
}
for (const auto & [u, v] : s.edges)
{
ah_range_error_if(u >= s.nodes.size() or v >= s.nodes.size())
<< "Edge endpoint out of range";
size_t a = u;
size_t b = v;
if (a > b)
swap(a, b);
if (matched_set.search(make_pair(a, b)) != nullptr)
continue;
out << " \\draw[edge] (n" << u << ") -- (n" << v << ");\n";
}
for (auto [u, v] : matching_pairs)
out << " \\draw[match] (n" << u << ") -- (n" << v << ");\n";
out << " \\node[font=\\small] at (0,-2.95) {matching size = "
<< matching_pairs.size() << "};\n";
out << "\\end{tikzpicture}\n";
out << "\\end{document}\n";
}
template <class GT>
pair<size_t, Array<pair<size_t, size_t>>> solve_case(const Scenario & s)
{
GT g = build_graph<GT>(s);
const size_t cardinality = blossom_maximum_cardinality_matching<GT>(g, matching);
return std::make_pair(cardinality, extract_matching_pairs(g, matching));
}
template <class GT>
void print_backend_result(const string & backend_name,
const Scenario & s,
size_t & cardinality_ref,
bool & is_first)
{
auto [cardinality, pairs] = solve_case<GT>(s);
if (is_first)
{
cardinality_ref = cardinality;
pairs_ref = pairs;
is_first = false;
}
cout << " " << backend_name << " -> size " << cardinality
<< " | " << format_pairs(pairs) << "\n";
if (cardinality != cardinality_ref)
cerr << " Warning: cardinality mismatch across backends\n";
}
void run_scenario(const Scenario & s)
{
cout << "\n" << s.title << "\n";
cout << string(s.title.size(), '-') << "\n";
size_t cardinality = 0;
Array<pair<size_t, size_t>> canonical_pairs;
bool first = true;
print_backend_result<List_Graph<Graph_Node<int>, Graph_Arc<int>>>(
"List_Graph", s, cardinality, canonical_pairs, first);
print_backend_result<List_SGraph<Graph_Snode<int>, Graph_Sarc<int>>>(
"List_SGraph", s, cardinality, canonical_pairs, first);
print_backend_result<Array_Graph<Graph_Anode<int>, Graph_Aarc<int>>>(
"Array_Graph", s, cardinality, canonical_pairs, first);
const string tex_path = "/tmp/blossom_" + s.slug + ".tex";
write_tikz(s, canonical_pairs, tex_path);
cout << " TikZ export: " << tex_path << "\n";
}
}
int main()
{
const Scenario pentagon_stems{
.slug = "pentagon_stems",
.title = "Blossom demo: odd cycle + stems",
.nodes = {
{-1.90, 1.10, "0"},
{ 0.00, 2.20, "1"},
{ 1.90, 1.10, "2"},
{ 1.30, -1.10, "3"},
{-1.30, -1.10, "4"},
{-3.10, 2.35, "5"},
{ 3.10, 2.35, "6"},
{ 2.70, -2.05, "7"}
},
.edges = {
{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0},
{1, 5}, {2, 6}, {3, 7}
}
};
const Scenario double_flower{
.slug = "double_flower",
.title = "Blossom demo: two odd flowers",
.nodes = {
{-3.00, 1.40, "0"},
{-1.60, 2.40, "1"},
{-0.40, 1.20, "2"},
{-0.70, -0.60, "3"},
{-2.40, -0.90, "4"},
{ 0.80, 0.10, "5"},
{ 2.10, 1.60, "6"},
{ 3.20, 0.20, "7"},
{ 2.40, -1.50, "8"},
{ 0.30, 2.55, "9"}
},
.edges = {
{0, 1}, {1, 2}, {2, 3}, {3, 4}, {4, 0},
{4, 5}, {5, 6}, {6, 7}, {7, 8}, {8, 5},
{2, 5}, {3, 7}, {1, 9}, {6, 9}
}
};
cout << "Edmonds-Blossom maximum matching examples\n";
cout << "========================================\n";
run_scenario(pentagon_stems);
run_scenario(double_flower);
cout << "\nTo compile a TikZ file to PDF (optional):\n";
cout << " cd /tmp && pdflatex blossom_pentagon_stems.tex\n";
cout << " cd /tmp && pdflatex blossom_double_flower.tex\n";
return 0;
}
Edmonds' Blossom algorithm for maximum matching in general graphs.
#define ah_range_error_if(C)
Throws std::range_error if condition holds.
Definition ah-errors.H:212
int main()
size_t size_t int32_t * out
Definition ca-c-api.h:120
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:250
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Dynamic doubly linked list with O(1) size and bidirectional access.
Dynamic set backed by balanced binary search trees with automatic memory management.
Key * insert(const Key &key)
Inserts a key into the dynamic set.
Key * search(const Key &key) const
Find an element in the set.
Arc for graphs implemented with simple adjacency lists.
Definition tpl_sgraph.H:197
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:525
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:605
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:779
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:228
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::pair< First, Second > pair
Alias to std::pair kept for backwards compatibility.
Definition ahPair.H:89
STL namespace.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:223
Array-based graph implementation.
Dynamic array container with automatic resizing.
Dynamic set implementations based on balanced binary search trees.
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.