Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
lca_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
43#include <array>
44#include <chrono>
45#include <cstddef>
46#include <cstdint>
47#include <iomanip>
48#include <iostream>
49#include <random>
50#include <utility>
51
52#include <LCA.H>
53#include <tpl_array.H>
54#include <tpl_agraph.H>
55#include <tpl_graph.H>
56#include <tpl_sgraph.H>
57
58using namespace Aleph;
59
60namespace
61{
62 enum Node_Id : size_t
63 {
64 CEO = 0,
65 CTO = 1,
66 CFO = 2,
67 PLATFORM = 3,
68 DATA = 4,
69 ACCOUNTING = 5,
70 LEGAL = 6,
71 SRE = 7,
72 ML = 8,
73 NODES = 9
74 };
75
76 static const std::array<const char *, NODES> NODE_LABELS = {
77 "CEO", "CTO", "CFO", "Platform", "Data",
78 "Accounting", "Legal", "SRE", "ML"
79 };
80
81 struct Query
82 {
83 size_t u;
84 size_t v;
85 size_t expected_lca;
86 size_t expected_distance;
87 };
88
89 static const std::array<Query, 6> QUERIES = {{
90 {SRE, ML, CTO, 4},
91 {SRE, DATA, CTO, 3},
92 {ACCOUNTING, LEGAL, CFO, 2},
93 {ACCOUNTING, ML, CEO, 5},
94 {PLATFORM, CTO, CTO, 1},
95 {CTO, CFO, CEO, 2}
96 }};
97
98 struct Backend_Result
99 {
100 const char * backend_name = "";
101 bool deterministic_ok = true;
102 bool engines_agree = true;
103 bool checksum_equal = true;
104 std::uint64_t binary_ms = 0;
105 std::uint64_t euler_ms = 0;
106 std::uint64_t checksum_bin = 0;
107 std::uint64_t checksum_eul = 0;
108 };
109
123 template <class GT>
124 Backend_Result run_backend(const char * backend_name)
125 {
126 using Graph = GT;
127 using Node = typename Graph::Node;
128
129 Graph g;
131 for (size_t i = 0; i < NODES; ++i)
132 nodes(i) = g.insert_node(static_cast<int>(i));
133
134 // "Company hierarchy" as a rooted tree.
135 g.insert_arc(nodes(CEO), nodes(CTO), 1);
136 g.insert_arc(nodes(CEO), nodes(CFO), 1);
138 g.insert_arc(nodes(CTO), nodes(DATA), 1);
140 g.insert_arc(nodes(CFO), nodes(LEGAL), 1);
142 g.insert_arc(nodes(DATA), nodes(ML), 1);
143
146
147 Backend_Result result;
148 result.backend_name = backend_name;
149
150 std::cout << "Backend: " << backend_name << "\n";
151 std::cout << std::left
152 << std::setw(18) << "Query(u,v)"
153 << std::setw(14) << "Binary LCA"
154 << std::setw(14) << "Euler LCA"
155 << std::setw(10) << "Distance"
156 << "Status\n";
157 std::cout << std::string(66, '-') << "\n";
158
159 for (const auto & q : QUERIES)
160 {
161 Node * lca_bin = binary_lca.lca(nodes(q.u), nodes(q.v));
162 Node * lca_eul = euler_lca.lca(nodes(q.u), nodes(q.v));
163
164 const auto bl_idx = static_cast<size_t>(lca_bin->get_info());
165 const auto er_idx = static_cast<size_t>(lca_eul->get_info());
166 const size_t dist = binary_lca.distance(nodes(q.u), nodes(q.v));
167
168 const bool agree = (lca_bin == lca_eul);
169 const bool expected = (bl_idx == q.expected_lca) and
170 (er_idx == q.expected_lca) and
171 (dist == q.expected_distance);
172
173 result.engines_agree = result.engines_agree and agree;
174 result.deterministic_ok = result.deterministic_ok and expected;
175
176 std::cout << std::left
177 << std::setw(18)
178 << (std::string(NODE_LABELS[q.u]) + "," + NODE_LABELS[q.v])
179 << std::setw(14) << NODE_LABELS[bl_idx]
180 << std::setw(14) << NODE_LABELS[er_idx]
181 << std::setw(10) << dist
182 << ((agree and expected) ? "OK" : "MISMATCH")
183 << "\n";
184 }
185
186 constexpr size_t qcount = 200000;
188 std::mt19937 rng(0x1ca2026u);
189 std::uniform_int_distribution<size_t> pick(0, NODES - 1);
190 for (size_t i = 0; i < qcount; ++i)
191 random_queries(i) = std::make_pair(pick(rng), pick(rng));
192
193 auto run_binary = [&]() -> std::pair<std::uint64_t, std::uint64_t>
194 {
195 const auto t0 = std::chrono::high_resolution_clock::now();
196 std::uint64_t checksum = 0;
197 for (size_t i = 0; i < qcount; ++i)
198 {
199 const auto [u, v] = random_queries(i);
200 checksum += static_cast<std::uint64_t>(binary_lca.lca(nodes(u), nodes(v))->get_info() + 1);
201 }
202 const auto t1 = std::chrono::high_resolution_clock::now();
203 const auto ms = static_cast<std::uint64_t>(
204 std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count());
205 return std::make_pair(ms, checksum);
206 };
207
208 auto run_euler = [&]() -> std::pair<std::uint64_t, std::uint64_t>
209 {
210 const auto t0 = std::chrono::high_resolution_clock::now();
211 std::uint64_t checksum = 0;
212 for (size_t i = 0; i < qcount; ++i)
213 {
214 const auto [u, v] = random_queries(i);
215 checksum += static_cast<std::uint64_t>(euler_lca.lca(nodes(u), nodes(v))->get_info() + 1);
216 }
217 const auto t1 = std::chrono::high_resolution_clock::now();
218 const auto ms = static_cast<std::uint64_t>(
219 std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0).count());
220 return std::make_pair(ms, checksum);
221 };
222
223 const auto [binary_ms, checksum_bin] = run_binary();
224 const auto [euler_ms, checksum_eul] = run_euler();
225
226 result.binary_ms = binary_ms;
227 result.euler_ms = euler_ms;
228 result.checksum_bin = checksum_bin;
229 result.checksum_eul = checksum_eul;
230 result.checksum_equal = (checksum_bin == checksum_eul);
231
232 std::cout << "Benchmark (" << qcount << " random queries):\n";
233 std::cout << " Binary lifting: " << binary_ms << " ms\n";
234 std::cout << " Euler + RMQ: " << euler_ms << " ms\n";
235 std::cout << " Checksums equal: " << (result.checksum_equal ? "yes" : "no") << "\n\n";
236
237 return result;
238 }
239}
240
250 int main()
251 {
255
256 std::cout << "==============================================================\n";
257 std::cout << " LCA on Aleph Tree Graphs (Cross-Backend Parity Demo)\n";
258 std::cout << "==============================================================\n\n";
259
260 const auto list_result = run_backend<LG>("List_Graph");
261 const auto sgraph_result = run_backend<SG>("List_SGraph");
262 const auto agraph_result = run_backend<AG>("Array_Graph");
263
264 std::cout << "Parity summary:\n";
265 std::cout << std::left
266 << std::setw(13) << "Backend"
267 << std::setw(14) << "Deterministic"
268 << std::setw(14) << "EnginesAgree"
269 << std::setw(14) << "ChecksumEq"
270 << std::setw(11) << "Binary(ms)"
271 << "Euler(ms)\n";
272 std::cout << std::string(72, '-') << "\n";
273
274 bool any_failure = false;
275 const std::array<Backend_Result, 3> results = {list_result, sgraph_result, agraph_result};
276 for (const auto & r : results)
277 {
278 std::cout << std::left
279 << std::setw(13) << r.backend_name
280 << std::setw(14) << (r.deterministic_ok ? "yes" : "no")
281 << std::setw(14) << (r.engines_agree ? "yes" : "no")
282 << std::setw(14) << (r.checksum_equal ? "yes" : "no")
283 << std::setw(11) << r.binary_ms
284 << r.euler_ms << "\n";
285
286 if (not r.deterministic_ok or not r.engines_agree or not r.checksum_equal)
287 any_failure = true;
288 }
289
290 const bool cross_backend_checksum_ok =
291 list_result.checksum_bin == sgraph_result.checksum_bin and
292 list_result.checksum_bin == agraph_result.checksum_bin;
293
294 std::cout << "\nCross-backend checksum parity: "
295 << (cross_backend_checksum_ok ? "yes" : "no") << "\n";
296
298 any_failure = true;
299
300 return any_failure ? 1 : 0;
301 }
302
Lowest Common Ancestor (LCA) on rooted trees represented as Aleph graphs.
WeightedDigraph::Node Node
List_Graph< Graph_Node< Node_Info >, Graph_Arc< Arc_Info > > GT
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:194
LCA via binary lifting on a rooted tree.
Definition LCA.H:440
LCA via Euler tour + RMQ on depth in a rooted tree.
Definition LCA.H:692
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 Node
The graph type.
Definition tpl_graph.H:432
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Graph class implemented with singly-linked adjacency lists.
Definition tpl_sgraph.H:274
static mt19937 rng
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
int main()
Runs the LCA cross-backend parity demo and prints results.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
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.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
gsl_rng * r
Array-based graph implementation.
Dynamic array container with automatic resizing.
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.