Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
weighted_blossom_example.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
55#include <algorithm>
56#include <fstream>
57#include <iomanip>
58#include <iostream>
59#include <string>
60#include <utility>
61
62#include <Blossom_Weighted.H>
63#include <tpl_agraph.H>
64#include <tpl_dynArray.H>
65#include <tpl_dynSetTree.H>
66#include <tpl_graph.H>
67#include <tpl_sgraph.H>
68
69using namespace Aleph;
70
71namespace
72{
73 struct Node_Pos
74 {
75 double x = 0;
76 double y = 0;
77 std::string label;
78 };
79
80
81 struct Weighted_Edge
82 {
83 size_t u = 0;
84 size_t v = 0;
85 long long w = 0;
86 };
87
88
89 struct Scenario
90 {
91 std::string slug;
92 std::string title;
95 };
96
97
98 struct Solve_Output
99 {
100 long long total_weight = 0;
101 size_t cardinality = 0;
103 };
104
105
118 template <class GT>
119 GT build_graph(const Scenario & s)
120 {
121 GT g;
123
124 for (size_t i = 0; i < s.nodes.size(); ++i)
125 nodes.append(g.insert_node(static_cast<int>(i)));
126
127 for (const auto & e : s.edges)
128 g.insert_arc(nodes(e.u), nodes(e.v), e.w);
129
130 return g;
131 }
132
133
147 template <class GT>
148 Solve_Output solve_case(const Scenario & s, bool max_cardinality)
149 {
150 GT g = build_graph<GT>(s);
151
153 const auto result = blossom_maximum_weight_matching<GT>(
154 g,
155 matching,
156 Dft_Dist<GT>(),
159
161 for (auto it = matching.get_it(); it.has_curr(); it.next_ne())
162 {
163 auto * arc = it.get_curr();
164 size_t u = static_cast<size_t>(g.get_src_node(arc)->get_info());
165 size_t v = static_cast<size_t>(g.get_tgt_node(arc)->get_info());
166 if (u > v)
167 std::swap(u, v);
168 sorted_pairs.insert(std::make_pair(u, v));
169 }
170
172 for (auto it = sorted_pairs.get_it(); it.has_curr(); it.next_ne())
173 pairs.append(it.get_curr());
174
175 return Solve_Output{result.total_weight, result.cardinality, pairs};
176 }
177
178
188 std::string format_pairs(const DynArray<std::pair<size_t, size_t>> & pairs)
189 {
190 if (pairs.is_empty())
191 return "(empty)";
192
193 std::string out;
194 for (size_t i = 0; i < pairs.size(); ++i)
195 {
196 const auto & p = pairs(i);
197 out += "(" + std::to_string(p.first) + "," + std::to_string(p.second) + ")";
198 if (i + 1 < pairs.size())
199 out += " ";
200 }
201 return out;
202 }
203
204
219 void write_tikz(const Scenario & s,
220 const Solve_Output & output,
221 bool max_cardinality,
222 const std::string & file_path)
223 {
225
226 std::ofstream out(file_path);
227 if (not out)
228 {
229 std::cerr << "Cannot write file: " << file_path << "\n";
230 return;
231 }
232
233 out << "\\documentclass[tikz,border=10pt]{standalone}\n";
234 out << "\\usepackage{tikz}\n";
235 out << "\\usetikzlibrary{arrows.meta,calc}\n";
236 out << "\\begin{document}\n";
237 out << "\\begin{tikzpicture}[x=1cm,y=1cm]\n";
238
239 out << " \\path[shade, left color=cyan!6, right color=blue!5] "
240 << "(-4.6,-3.4) rectangle (4.6,3.4);\n";
241
242 out << " \\node[font=\\bfseries\\large] at (0,3.05) {"
243 << s.title << "};\n";
244
245 out << " \\node[font=\\small] at (0,2.65) {objective: "
246 << (max_cardinality ? "max-cardinality then max-weight" : "pure max-weight")
247 << "};\n";
248
249 out << " \\tikzset{"
250 << "edge/.style={draw=black!58, line width=0.95pt, shorten <=2pt, shorten >=2pt},"
251 << "match/.style={draw=orange!92!black, line width=2.7pt},"
252 << "wlabel/.style={fill=white, inner sep=1.2pt, font=\\scriptsize},"
253 << "vertex/.style={circle, draw=black!70, fill=white, minimum size=9mm, inner sep=1pt, font=\\small\\bfseries}"
254 << "}\n";
255
256 // Emit vertex node definitions first so \draw commands can reference them.
257 for (size_t i = 0; i < s.nodes.size(); ++i)
258 {
259 const auto & p = s.nodes(i);
260 out << " \\node[vertex] (n" << i << ") at ("
261 << std::fixed << std::setprecision(2) << p.x << "," << p.y << ") {"
262 << p.label << "};\n";
263 }
264
265 for (const auto & [u_raw, v_raw, w] : s.edges)
266 {
267 size_t u = u_raw;
268 size_t v = v_raw;
269 size_t a = std::min(u, v);
270 size_t b = std::max(u, v);
271 const bool matched = matched_set.contains(std::make_pair(a, b));
272
273 out << " \\draw[" << (matched ? "match" : "edge") << "] (n"
274 << u << ") -- (n" << v << ");\n";
275
276 out << " \\node[wlabel] at ($(n" << u << ")!0.5!(n" << v << ")$) {"
277 << w << "};\n";
278 }
279
280 out << " \\node[font=\\small] at (0,-3.05) {matching size = "
281 << output.cardinality
282 << ", total weight = " << output.total_weight << "};\n";
283
284 out << "\\end{tikzpicture}\n";
285 out << "\\end{document}\n";
286 }
287
288
302 template <class GT>
303 void print_backend(const std::string & backend,
304 const Scenario & s,
305 bool max_cardinality,
306 Solve_Output & reference,
307 bool & first)
308 {
309 const Solve_Output got = solve_case<GT>(s, max_cardinality);
310
311 if (first)
312 {
313 reference = got;
314 first = false;
315 }
316
317 std::cout << " " << backend
318 << " -> card " << got.cardinality
319 << ", weight " << got.total_weight
320 << " | " << format_pairs(got.matched_pairs) << "\n";
321
322 if (got.cardinality != reference.cardinality
323 or got.total_weight != reference.total_weight)
324 {
325 std::cerr << " Warning: backend objective mismatch\n";
326 }
327 }
328
329
337 void run_scenario(const Scenario & s)
338 {
339 std::cout << "\n" << s.title << "\n";
340 std::cout << std::string(s.title.size(), '-') << "\n";
341
342 for (bool max_cardinality : {false, true})
343 {
344 std::cout << (max_cardinality
345 ? "\nMode: maximum-cardinality then maximum-weight\n"
346 : "\nMode: pure maximum-weight\n");
347
348 Solve_Output canonical;
349 bool first = true;
350
352 "List_Graph", s, max_cardinality, canonical, first);
353
355 "List_SGraph", s, max_cardinality, canonical, first);
356
358 "Array_Graph", s, max_cardinality, canonical, first);
359
360 const std::string suffix = max_cardinality ? "max_cardinality" : "max_weight";
361 const std::string tex_path = "/tmp/weighted_blossom_" + s.slug + "_" + suffix + ".tex";
362
364 std::cout << " TikZ export: " << tex_path << "\n";
365 }
366 }
367 }
368
369
380 int main()
381
382{
383 const Scenario odd_cycle_bridge{
384 .slug = "odd_cycle_bridge",
385 .title = "Weighted blossom demo: odd cycle + bridge",
386 .nodes = {
387 {-2.20, 1.30, "0"},
388 {-0.50, 2.30, "1"},
389 { 1.30, 1.65, "2"},
390 { 1.55, -0.35, "3"},
391 {-0.20, -1.50, "4"},
392 {-2.05, -0.70, "5"},
393 { 3.35, 1.10, "6"},
394 { 3.05, -1.05, "7"}
395 },
396 .edges = {
397 {0, 1, 9},
398 {1, 2, 10},
399 {2, 3, 8},
400 {3, 4, 7},
401 {4, 5, 9},
402 {5, 0, 8},
403 {1, 5, 6},
404 {2, 6, 12},
405 {3, 7, 11},
406 {6, 7, 5},
407 {0, 4, 4}
408 }
409 };
410
411 const Scenario tradeoff{
412 .slug = "tradeoff",
413 .title = "Weighted blossom demo: weight/cardinality trade-off",
414 .nodes = {
415 {-2.35, 0.95, "0"},
416 {-0.85, 2.05, "1"},
417 { 0.95, 2.00, "2"},
418 { 2.35, 0.85, "3"},
419 { 1.50, -1.25, "4"},
420 {-0.40, -1.95, "5"}
421 },
422 .edges = {
423 {0, 1, 2},
424 {0, 4, 3},
425 {1, 2, 7},
426 {1, 5, 2},
427 {2, 3, 9},
428 {2, 5, 4},
429 {3, 4, 8},
430 {3, 5, 4}
431 }
432 };
433
436
437 std::cout << "\nDone. Compile generated .tex files with pdflatex to visualize the matchings.\n";
438
439 return 0;
440}
Maximum-weight matching in general undirected graphs.
long double w
Definition btreepic.C:153
Default distance accessor for arc weights.
size_t size() const noexcept
Return the current dimension of array.
T & append()
Allocate a new entry to the end of array.
bool is_empty() const noexcept
Return true if the array is empty.
Dynamic doubly linked list with O(1) size and bidirectional access.
Dynamic set backed by balanced binary search trees with automatic memory management.
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:524
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:737
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:743
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
static void suffix(Node *root, DynList< Node * > &acc)
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Array-based graph implementation.
Lazy and scalable dynamic array implementation.
Dynamic set implementations based on balanced binary search trees.
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.
int main()
Runs example scenarios demonstrating maximum-weight matching and writes TikZ visualizations.
ofstream output
Definition writeHeap.C:215