Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tree_dp_test.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
37# include <gtest/gtest.h>
38
39# include <limits>
40# include <random>
41# include <utility>
42
43# include <Tree_DP.H>
44
45using namespace Aleph;
46
48
49namespace
50{
52 brute_sum_distances(const size_t n,
53 const Array<std::pair<size_t, size_t>> & edges)
54 {
56 adj.reserve(n);
57 for (size_t i = 0; i < n; ++i)
58 adj.append(Array<size_t>());
59
60 for (size_t i = 0; i < edges.size(); ++i)
61 {
62 const size_t u = edges[i].first;
63 const size_t v = edges[i].second;
64 adj(u).append(v);
65 adj(v).append(u);
66 }
67
69 for (size_t src = 0; src < n; ++src)
70 {
72 for (size_t i = 0; i < n; ++i)
73 dist(i) = std::numeric_limits<size_t>::max();
74
76 q.append(src);
77 dist(src) = 0;
78
79 for (size_t head = 0; head < q.size(); ++head)
80 {
81 const size_t u = q[head];
82 for (size_t j = 0; j < adj[u].size(); ++j)
83 {
84 const size_t v = adj[u][j];
85 if (dist[v] != std::numeric_limits<size_t>::max())
86 continue;
87 dist(v) = dist[u] + 1;
88 q.append(v);
89 }
90 }
91
92 size_t sum = 0;
93 for (size_t i = 0; i < n; ++i)
94 sum += dist[i];
95 sums(src) = sum;
96 }
97 return sums;
98 }
99} // namespace
100
101
102// Helper to build a path tree: 0 -- 1 -- 2 -- ... -- (n-1)
104{
105 G g;
107 for (size_t i = 0; i < n; ++i)
108 nodes(i) = g.insert_node(static_cast<int>(i));
109 for (size_t i = 0; i + 1 < n; ++i)
110 g.insert_arc(nodes(i), nodes(i + 1), 1);
111 return g;
112}
113
114// Helper to build a star tree: center=0, leaves=1..n-1
116{
117 G g;
119 for (size_t i = 0; i < n; ++i)
120 nodes(i) = g.insert_node(static_cast<int>(i));
121 for (size_t i = 1; i < n; ++i)
122 g.insert_arc(nodes(0), nodes(i), 1);
123 return g;
124}
125
126
127// ── Subtree sizes ─────────────────────────────────────────────────
128
130{
132 auto g = build_path(5, nodes);
133 // Root at node 0: 0-1-2-3-4
134 // subtree(0) = 5, subtree(1) = 4, ..., subtree(4) = 1
135 auto sizes = tree_subtree_sizes(g, nodes(0));
136 EXPECT_EQ(sizes.size(), 5u);
137
138 // Find the ids. Use Tree_DP to get the mapping.
140 [](auto *) -> size_t { return 1; },
141 [](auto *, const size_t & a, auto *, const size_t & c) -> size_t {
142 return a + c;
143 });
144
145 for (size_t i = 0; i < 5; ++i)
146 EXPECT_EQ(dp.value(nodes(i)), 5 - i)
147 << "Subtree of node " << i;
148}
149
151{
153 auto g = build_star(5, nodes);
154 // Root at center: subtree(center) = 5, subtree(leaf) = 1
156 [](auto *) -> size_t { return 1; },
157 [](auto *, const size_t & a, auto *, const size_t & c) -> size_t {
158 return a + c;
159 });
160
161 EXPECT_EQ(dp.value(nodes(0)), 5u);
162 for (size_t i = 1; i < 5; ++i)
163 EXPECT_EQ(dp.value(nodes(i)), 1u);
164}
165
167{
168 G g;
169 auto n = g.insert_node(0);
170 auto sizes = tree_subtree_sizes(g, n);
171 EXPECT_EQ(sizes.size(), 1u);
172 EXPECT_EQ(sizes[0], 1u);
173}
174
175
176// ── Max distance (eccentricity via rerooting) ─────────────────────
177
179{
181 auto g = build_path(5, nodes);
182 // Path 0-1-2-3-4: max distance from each node
183 // node 0: max dist = 4
184 // node 1: max dist = 3
185 // node 2: max dist = 2
186 // node 3: max dist = 3
187 // node 4: max dist = 4
188 auto dists = tree_max_distance(g, nodes(0));
189
191 size_t(0),
192 [](auto *) -> size_t { return 0; },
193 [](const size_t & a, const size_t & b) -> size_t {
194 return std::max(a, b);
195 },
196 [](auto *, auto *, const size_t & v) -> size_t { return v + 1; });
197
198 EXPECT_EQ(dp.value(nodes(0)), 4u);
199 EXPECT_EQ(dp.value(nodes(1)), 3u);
200 EXPECT_EQ(dp.value(nodes(2)), 2u);
201 EXPECT_EQ(dp.value(nodes(3)), 3u);
202 EXPECT_EQ(dp.value(nodes(4)), 4u);
203}
204
206{
208 auto g = build_star(5, nodes);
209 // Star with 5 nodes: center distance = 1, leaves distance = 2
211 size_t(0),
212 [](auto *) -> size_t { return 0; },
213 [](const size_t & a, const size_t & b) -> size_t {
214 return std::max(a, b);
215 },
216 [](auto *, auto *, const size_t & v) -> size_t { return v + 1; });
217
218 EXPECT_EQ(dp.value(nodes(0)), 1u);
219 for (size_t i = 1; i < 5; ++i)
220 EXPECT_EQ(dp.value(nodes(i)), 2u);
221}
222
223
224// ── Sum of distances (rerooting) ──────────────────────────────────
225
227{
229 auto g = build_path(4, nodes);
230 // Path 0-1-2-3
231 // sum_dist(0) = 0+1+2+3 = 6
232 // sum_dist(1) = 1+0+1+2 = 4
233 // sum_dist(2) = 2+1+0+1 = 4
234 // sum_dist(3) = 3+2+1+0 = 6
235 auto dists = tree_sum_of_distances(g, nodes(0));
236
237 using P = std::pair<size_t, size_t>;
238 Gen_Reroot_DP<G, P> dp(g, nodes(0),
239 P{0, 0},
240 [](auto *) -> P { return {1, 0}; },
241 [](const P & a, const P & b) -> P {
242 return {a.first + b.first, a.second + b.second};
243 },
244 [](auto *, auto *, const P & v) -> P {
245 return {v.first, v.second + v.first};
246 });
247
248 EXPECT_EQ(dp.value(nodes(0)).second, 6u);
249 EXPECT_EQ(dp.value(nodes(1)).second, 4u);
250 EXPECT_EQ(dp.value(nodes(2)).second, 4u);
251 EXPECT_EQ(dp.value(nodes(3)).second, 6u);
252}
253
254
255// ── Error handling ────────────────────────────────────────────────
256
258{
259 // Create a graph with a cycle
260 G g;
261 auto n0 = g.insert_node(0);
262 auto n1 = g.insert_node(1);
263 auto n2 = g.insert_node(2);
264 g.insert_arc(n0, n1, 1);
265 g.insert_arc(n1, n2, 1);
266 g.insert_arc(n2, n0, 1); // creates cycle
267
268 EXPECT_THROW(tree_subtree_sizes(g, n0), std::domain_error);
269}
270
272{
273 G g;
274 auto n0 = g.insert_node(0);
275 auto n1 = g.insert_node(1);
276 auto n2 = g.insert_node(2);
277 auto n3 = g.insert_node(3);
278 g.insert_arc(n0, n1, 1);
279 g.insert_arc(n2, n3, 1);
280 EXPECT_THROW(tree_subtree_sizes(g, n0), std::domain_error);
281}
282
284{
285 G g1;
286 auto r1 = g1.insert_node(1);
287 (void) r1;
288
289 G g2;
290 auto external_root = g2.insert_node(42);
291 EXPECT_THROW(tree_subtree_sizes(g1, external_root), std::domain_error);
292}
293
295{
296 G g;
297 auto n0 = g.insert_node(0);
298 auto n1 = g.insert_node(1);
299 auto n2 = g.insert_node(2);
300 g.insert_arc(n0, n1, 1);
301 g.insert_arc(n0, n1, 1); // duplicate edge
302 g.insert_arc(n1, n2, 1);
303
304 Gen_Tree_DP<G, size_t> dp(g, n0,
305 [](auto *) -> size_t { return 1; },
306 [](auto *, const size_t & a, auto *, const size_t & c) -> size_t {
307 return a + c;
308 });
309
310 EXPECT_EQ(dp.value(n0), 3u);
311 EXPECT_EQ(dp.value(n1), 2u);
312 EXPECT_EQ(dp.value(n2), 1u);
313}
314
316{
317 // 0
318 // / |
319 // 1 2
320 // / |
321 // 3 4
322 G g;
323 auto n0 = g.insert_node(0);
324 auto n1 = g.insert_node(1);
325 auto n2 = g.insert_node(2);
326 auto n3 = g.insert_node(3);
327 auto n4 = g.insert_node(4);
328 g.insert_arc(n0, n1, 1);
329 g.insert_arc(n0, n2, 1);
330 g.insert_arc(n1, n3, 1);
331 g.insert_arc(n1, n4, 1);
332
333 Gen_Tree_DP<G, size_t> dp(g, n0,
334 [](auto *) -> size_t { return 1; },
335 [](auto *, const size_t & a, auto *, const size_t & c) -> size_t {
336 return a + c;
337 });
338
339 EXPECT_EQ(dp.value(n0), 5u);
340 EXPECT_EQ(dp.value(n1), 3u);
341 EXPECT_EQ(dp.value(n2), 1u);
342 EXPECT_EQ(dp.value(n3), 1u);
343 EXPECT_EQ(dp.value(n4), 1u);
344}
345
347{
348 std::mt19937 rng(20260226);
349
350 for (int trial = 0; trial < 80; ++trial)
351 {
352 const size_t n = 2 + rng() % 40;
353 G g;
355 for (size_t i = 0; i < n; ++i)
356 nodes(i) = g.insert_node(static_cast<int>(i));
357
359 edges.reserve(n - 1);
360 for (size_t i = 1; i < n; ++i)
361 {
362 const size_t p = static_cast<size_t>(rng() % i);
363 g.insert_arc(nodes(p), nodes(i), 1);
364 edges.append(std::make_pair(p, i));
365 }
366
367 const auto brute = brute_sum_distances(n, edges);
368 const auto fast = tree_sum_of_distances(g, nodes(0));
369
371 [](auto *) -> size_t { return 1; },
372 [](auto *, const size_t & a, auto *, const size_t & c) -> size_t {
373 return a + c;
374 });
375
376 for (size_t i = 0; i < n; ++i)
377 {
378 const size_t id = map.id_of(nodes(i));
379 EXPECT_EQ(fast[id], brute[i])
380 << "trial=" << trial << " node=" << i;
381 }
382 }
383}
Generic tree DP and rerooting DP on Aleph graph trees.
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
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:351
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:315
Rerooting DP: O(n) computation of DP answer for all roots.
Definition Tree_DP.H:462
Generic bottom-up tree DP.
Definition Tree_DP.H:333
size_t id_of(Node *node) const
Returns the internal ID for a given node pointer.
Definition Tree_DP.H:421
Graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:428
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
#define TEST(name)
static mt19937 rng
pair< size_t, string > P
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Array< size_t > tree_subtree_sizes(const GT &g, typename GT::Node *root, SA sa=SA())
Compute subtree sizes for every node.
Definition Tree_DP.H:647
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.
Array< size_t > tree_max_distance(const GT &g, typename GT::Node *root, SA sa=SA())
Compute the maximum distance from each node to any leaf.
Definition Tree_DP.H:680
Array< size_t > tree_sum_of_distances(const GT &g, typename GT::Node *root, SA sa=SA())
Compute sum of distances from each node to all others.
Definition Tree_DP.H:712
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
static G build_star(size_t n, Array< G::Node * > &nodes)
static G build_path(size_t n, Array< G::Node * > &nodes)