Peano
Loading...
Searching...
No Matches
test_splittrees_tree3_fine_first_bfs.py
Go to the documentation of this file.
1import pytest
2
3import toolbox.loadbalancing.api.strategies.SplitTrees as split_strategy
4import toolbox.loadbalancing.api.strategies.Decomposition as decomposition_strategy
5from tree_builders import build_tree3_adaptive_2d, build_tree3_all_rows_2d
6
7
8# ---------------------------------------------------------------------------
9# Stage I integration test
10# ---------------------------------------------------------------------------
11
12@pytest.mark.integration
14 """
15 Integration test for Stage I with traversal="bfs_finest_first".
16
17 Exercises the tree described by tree3_stage1_fine_first_bfs.dot:
18 - 23 leaves total (21 at the finest level + 2 childless level-1 leaves).
19 - BFS finest-first processes level 2 leaves before level 1 leaves.
20 - With 4 subtrees, the balanced distribution is [6, 6, 6, 5].
21
22 Expected leaf assignments after Stage I
23 (first-row positions at each level):
24 Level 3: (0,0)–(0,5) → subtree 0 [L3A1*, L3A2*]
25 (0,9)–(0,14) → subtree 1 [L3B1*, L3B2*]
26 (0,18)–(0,23)→ subtree 2 [L3C1*, L3C2*]
27 (0,24)–(0,26)→ subtree 3 [L3C3*]
28 Level 2: (0,2) → subtree 3 [L2A3, childless leaf]
29 (0,5) → subtree 3 [L2B3, childless leaf]
30 Non-leaf nodes (all levels) remain subtree 0.
31 """
32 tree = build_tree3_adaptive_2d()
33 decomposition = decomposition_strategy.equidistant_splitting(
34 peano_tree=tree,
35 number_of_subtrees=4,
36 )
37
38 split_strategy.split_tree(
39 peano_tree=tree,
40 decomposition=decomposition,
41 traversal="bfs_finest_first",
42 enable_propagate_bottom_up=False,
43 enable_optimize_hierarchy=False,
44 )
45
46 # --- Level 0: single root unchanged (non-leaf, no redistribution) ---
47 assert tree._tree_arrays[0][0][0] == (1, 0), "Root must stay subtree 0 (non-leaf)"
48
49 # --- Level 1: L1A, L1B, L1C unchanged (non-leaf, no redistribution) ---
50 assert tree._tree_arrays[1][0][0] == (1, 0), "L1A must stay subtree 0 (non-leaf)"
51 assert tree._tree_arrays[1][0][1] == (1, 0), "L1B must stay subtree 0 (non-leaf)"
52 assert tree._tree_arrays[1][0][2] == (1, 0), "L1C must stay subtree 0 (non-leaf)"
53
54 # --- Level 2: only the childless leaves (L2A3, L2B3) are redistributed ---
55 # Non-leaves (L2A1, L2A2, L2B1, L2B2, L2C1, L2C2, L2C3) stay subtree 0
56 for col in (0, 1, 3, 4, 6, 7, 8):
57 assert tree._tree_arrays[2][0][col] == (1, 0), (
58 f"Non-leaf L2 node at (0,{col}) must not be redistributed by Stage I"
59 )
60 # Childless leaves → subtree 3 (last bucket in [6,6,6,5] distribution)
61 assert tree._tree_arrays[2][0][2] == (1, 3), "L2A3 (childless leaf) must be subtree 3"
62 assert tree._tree_arrays[2][0][5] == (1, 3), "L2B3 (childless leaf) must be subtree 3"
63
64 # --- Level 3: 21 finest-level leaves in BFS order ---
65 # Subtree 0: first 6 (L3A1A/B/C + L3A2A/B/C)
66 for col in range(0, 6):
67 assert tree._tree_arrays[3][0][col] == (1, 0), f"L3A leaf (0,{col}) must be subtree 0"
68 # Subtree 1: next 6 (L3B1A/B/C + L3B2A/B/C)
69 for col in range(9, 15):
70 assert tree._tree_arrays[3][0][col] == (1, 1), f"L3B leaf (0,{col}) must be subtree 1"
71 # Subtree 2: next 6 (L3C1A/B/C + L3C2A/B/C)
72 for col in range(18, 24):
73 assert tree._tree_arrays[3][0][col] == (1, 2), f"L3C12 leaf (0,{col}) must be subtree 2"
74 # Subtree 3: last 3 at finest level (L3C3A/B/C); the final 2 count comes from level 1
75 for col in range(24, 27):
76 assert tree._tree_arrays[3][0][col] == (1, 3), f"L3C3 leaf (0,{col}) must be subtree 3"
77
78 # Gap cells (children of childless L2 nodes) must remain weight=0
79 for col in list(range(6, 9)) + list(range(15, 18)):
80 assert tree._tree_arrays[3][0][col] == (0, 0), (
81 f"Level-2 cell (0,{col}) under a childless L2 node must stay non-existent"
82 )
83
84 # Verify the balanced leaf-count summary [6, 6, 6, 5]
85 tree.update_split_pattern()
86 assert list(tree._split_pattern) == [6.0, 6.0, 6.0, 5.0], (
87 f"Expected [6,6,6,5] leaf distribution, got {list(tree._split_pattern)}"
88 )
89
90
91# ---------------------------------------------------------------------------
92# Stage I + II integration test
93# ---------------------------------------------------------------------------
94
95@pytest.mark.integration
97 """
98 Integration test for Stage I + Stage II with traversal="bfs_finest_first".
99
100 Exercises the tree described by tree3_stage2_fine_first_bfs.dot.
101 After Stage II (bottom-up propagation), parents whose *existing* children
102 all share the same subtree id are promoted to that subtree.
103
104 Changes introduced by Stage II on top of Stage I:
105 Level 1:
106 L2B1=(0,3): all children → subtree 1 → promoted from 0 to 1
107 L2B2=(0,4): all children → subtree 1 → promoted from 0 to 1
108 L2C1=(0,6): all children → subtree 2 → promoted from 0 to 2
109 L2C2=(0,7): all children → subtree 2 → promoted from 0 to 2
110 L2C3=(0,8): all children → subtree 3 → promoted from 0 to 3
111 L2A1=(0,0): all children → subtree 0 → stays 0 (no change)
112 L2A2=(0,1): all children → subtree 0 → stays 0 (no change)
113 L2A3=(0,2): no existing children → stays subtree 3 (Stage I result)
114 L2B3=(0,5): no existing children → stays subtree 3 (Stage I result)
115
116 Level 1:
117 L1A=(0,0): children {0,3} (L2A1=0, L2A2=0, L2A3=3) → mixed → stays 0
118 L1B=(0,1): children {1,3} (L2B1=1, L2B2=1, L2B3=3) → mixed → stays 0
119 L1C=(0,2): children {2,3} (L2C1=2, L2C2=2, L2C3=3) → mixed → stays 0
120
121 Level 0:
122 Root=(0,0): children {0} (L1A=0, L1B=0, L1C=0) at this stage → stays 0
123 """
124 tree_stage1 = build_tree3_adaptive_2d()
125 decomposition_stage1 = decomposition_strategy.equidistant_splitting(
126 peano_tree=tree_stage1,
127 number_of_subtrees=4,
128 )
129 split_strategy.split_tree(
130 peano_tree=tree_stage1,
131 decomposition=decomposition_stage1,
132 traversal="bfs_finest_first",
133 enable_propagate_bottom_up=False,
134 enable_optimize_hierarchy=False,
135 )
136
137 tree = build_tree3_adaptive_2d()
138 decomposition = decomposition_strategy.equidistant_splitting(
139 peano_tree=tree,
140 number_of_subtrees=4,
141 )
142
143 split_strategy.split_tree(
144 peano_tree=tree,
145 decomposition=decomposition,
146 traversal="bfs_finest_first",
147 enable_propagate_bottom_up=True,
148 enable_optimize_hierarchy=False,
149 )
150
151 # --- Level 0: root stays subtree 0 (only one existing child-subtree) ---
152 assert tree._tree_arrays[0][0][0] == (1, 0), "Root must stay 0"
153
154 # --- Level 1: all L1 nodes stay subtree 0 (mixed L2 children) ---
155 assert tree._tree_arrays[1][0][0] == (1, 0), "L1A must stay 0 (mixed children {0,3})"
156 assert tree._tree_arrays[1][0][1] == (1, 0), "L1B must stay 0 (mixed children {1,3})"
157 assert tree._tree_arrays[1][0][2] == (1, 0), "L1C must stay 0 (mixed children {2,3})"
158
159 # --- Level 2: promoted and unchanged nodes ---
160 assert tree._tree_arrays[2][0][0] == (1, 0), "L2A1 stays 0 (uniform subtree 0 children)"
161 assert tree._tree_arrays[2][0][1] == (1, 0), "L2A2 stays 0 (uniform subtree 0 children)"
162 assert tree._tree_arrays[2][0][2] == (1, 3), "L2A3 stays 3 (childless, set by Stage I)"
163 assert tree._tree_arrays[2][0][3] == (1, 1), "L2B1 promoted to 1 (all children subtree 1)"
164 assert tree._tree_arrays[2][0][4] == (1, 1), "L2B2 promoted to 1 (all children subtree 1)"
165 assert tree._tree_arrays[2][0][5] == (1, 3), "L2B3 stays 3 (childless, set by Stage I)"
166 assert tree._tree_arrays[2][0][6] == (1, 2), "L2C1 promoted to 2 (all children subtree 2)"
167 assert tree._tree_arrays[2][0][7] == (1, 2), "L2C2 promoted to 2 (all children subtree 2)"
168 assert tree._tree_arrays[2][0][8] == (1, 3), "L2C3 promoted to 3 (all children subtree 3)"
169
170 # --- Level 3: unchanged from Stage I (propagation only moves up) ---
171 for col in range(0, 6):
172 assert tree._tree_arrays[3][0][col] == (1, 0), f"L3A leaf (0,{col}) must stay subtree 0"
173 for col in range(9, 15):
174 assert tree._tree_arrays[3][0][col] == (1, 1), f"L3B leaf (0,{col}) must stay subtree 1"
175 for col in range(18, 24):
176 assert tree._tree_arrays[3][0][col] == (1, 2), f"L3C12 leaf (0,{col}) must stay subtree 2"
177 for col in range(24, 27):
178 assert tree._tree_arrays[3][0][col] == (1, 3), f"L3C3 leaf (0,{col}) must stay subtree 3"
179
180
181# ---------------------------------------------------------------------------
182# Stage I + II + III integration test
183# ---------------------------------------------------------------------------
184
185@pytest.mark.integration
187 """
188 Integration test for Stage I + Stage II + Stage III (full pipeline).
189
190 Exercises the tree described by tree3_stage3_fine_first_bfs.dot using
191 a single-root Level 0 representation of the tree3 structure.
192
193 Even with a single root at Level 0, this first-row-only 2D embedding does
194 not satisfy Stage III's promotion precondition at the root:
195 the root has only 3 existing Level-1 children, while Stage III in 2D
196 requires all 3^2 = 9 children to exist.
197
198 Therefore Stage III should not change the root assignment.
199 """
200 # Baseline: Stage I + II (Stage III disabled)
201 tree_no_stage3 = build_tree3_adaptive_2d()
202 decomposition_no_stage3 = decomposition_strategy.equidistant_splitting(
203 peano_tree=tree_no_stage3,
204 number_of_subtrees=4,
205 )
206 split_strategy.split_tree(
207 peano_tree=tree_no_stage3,
208 decomposition=decomposition_no_stage3,
209 traversal="bfs_finest_first",
210 enable_propagate_bottom_up=True,
211 enable_optimize_hierarchy=False,
212 )
213
214 # Full pipeline: Stage I + II + III
215 tree_with_stage3 = build_tree3_adaptive_2d()
216 decomposition_with_stage3 = decomposition_strategy.equidistant_splitting(
217 peano_tree=tree_with_stage3,
218 number_of_subtrees=4,
219 )
220 split_strategy.split_tree(
221 peano_tree=tree_with_stage3,
222 decomposition=decomposition_with_stage3,
223 traversal="bfs_finest_first",
224 enable_propagate_bottom_up=True,
225 enable_optimize_hierarchy=True,
226 )
227
228 # Stage II baseline: root remains 0.
229 assert tree_no_stage3._tree_arrays[0][0][0] == (1, 0), "Root must be 0 after Stage II only"
230
231 # Stage III should not promote root, because only 3/9 children exist.
232 assert tree_with_stage3._tree_arrays[0][0][0] == (1, 0), (
233 "Root must stay 0: Stage III requires all 9 children in 2D"
234 )
235
236 # L1 nodes remain as Stage II leaves from the root's perspective.
237 assert tree_with_stage3._tree_arrays[1][0][0] == (1, 0), "L1A stays 0"
238 assert tree_with_stage3._tree_arrays[1][0][1] == (1, 0), "L1B stays 0"
239 assert tree_with_stage3._tree_arrays[1][0][2] == (1, 0), "L1C stays 0"
240
241
242# ---------------------------------------------------------------------------
243# All-rows representation variant (single-root Level 0)
244# ---------------------------------------------------------------------------
245
246@pytest.mark.integration
248 """
249 Stage I test for the all-rows representation variant.
250
251 In this all-rows recursive expansion variant, the represented tree is much
252 denser than the first-row embedding. For 4 subtrees, Stage I
253 deterministically distributes 585 leaves as [147, 146, 146, 146].
254 """
255 tree = build_tree3_all_rows_2d()
256 decomposition = decomposition_strategy.equidistant_splitting(
257 peano_tree=tree,
258 number_of_subtrees=4,
259 )
260
261 split_strategy.split_tree(
262 peano_tree=tree,
263 decomposition=decomposition,
264 traversal="bfs_finest_first",
265 enable_propagate_bottom_up=False,
266 enable_optimize_hierarchy=False,
267 )
268
269 # Root and level-1 non-leaf nodes remain untouched in Stage I
270 assert tree._tree_arrays[0][0][0] == (1, 0)
271 for a in range(3):
272 for b in range(3):
273 assert tree._tree_arrays[1][a][b] == (1, 0)
274
275 # Childless L2 stripes (A3 and B3 columns) should fall into the last subtree bucket
276 # because finest-level leaves are assigned first with bfs_finest_first traversal.
277 for a in range(9):
278 for b in (2, 5):
279 assert tree._tree_arrays[2][a][b] == (1, 3)
280
281 tree.update_split_pattern()
282 assert list(tree._split_pattern) == [147.0, 146.0, 146.0, 146.0]
283
284
285@pytest.mark.integration
287 """
288 Stage II test for the all-rows representation variant.
289
290 Stage II keeps the root in subtree 0 and propagates ownership into
291 non-leaf nodes according to uniform-child regions in this dense all-rows
292 representation.
293
294 Deterministic outcome after Stage II:
295 - Level 1 already contains propagated non-zero ownership in selected cells.
296 - Level 2 column ownership sets follow a fixed mixed/uniform pattern.
297 """
298 tree_stage1 = build_tree3_all_rows_2d()
299 decomposition_stage1 = decomposition_strategy.equidistant_splitting(
300 peano_tree=tree_stage1,
301 number_of_subtrees=4,
302 )
303 split_strategy.split_tree(
304 peano_tree=tree_stage1,
305 decomposition=decomposition_stage1,
306 traversal="bfs_finest_first",
307 enable_propagate_bottom_up=False,
308 enable_optimize_hierarchy=False,
309 )
310
311 tree = build_tree3_all_rows_2d()
312 decomposition = decomposition_strategy.equidistant_splitting(
313 peano_tree=tree,
314 number_of_subtrees=4,
315 )
316 split_strategy.split_tree(
317 peano_tree=tree,
318 decomposition=decomposition,
319 traversal="bfs_finest_first",
320 enable_propagate_bottom_up=True,
321 enable_optimize_hierarchy=False,
322 )
323
324 # Root stays 0 after Stage II.
325 assert tree._tree_arrays[0][0][0] == (1, 0)
326
327 # Level-1 ownership after Stage II (deterministic for this fixture)
328 expected_l1 = [
329 [0, 0, 2],
330 [0, 0, 0],
331 [0, 0, 3],
332 ]
333 for a in range(3):
334 for b in range(3):
335 assert tree._tree_arrays[1][a][b] == (1, expected_l1[a][b])
336
337 expected_l2_column_sets = {
338 0: {0},
339 1: {0, 1},
340 2: {3},
341 3: {1, 2},
342 4: {0, 1},
343 5: {3},
344 6: {2, 3},
345 7: {0, 2, 3},
346 8: {2, 3},
347 }
348 for b in range(9):
349 subtree_set = {
350 tree._tree_arrays[2][a][b][1]
351 for a in range(9)
352 if tree._tree_arrays[2][a][b][0] == 1
353 }
354 assert subtree_set == expected_l2_column_sets[b]
355
356
357@pytest.mark.integration
359 """
360 Stage III test for the all-rows representation variant.
361
362 With all-row expansion, Stage III updates Level-1 ownership while keeping
363 the root unchanged.
364
365 Deterministic Level-1 outcome after Stage III:
366 - row 0: [0, 3, 2]
367 - row 1: [0, 1, 3]
368 - row 2: [0, 1, 3]
369
370 Root remains subtree 0.
371 """
372 tree = build_tree3_all_rows_2d()
373 decomposition = decomposition_strategy.equidistant_splitting(
374 peano_tree=tree,
375 number_of_subtrees=4,
376 )
377
378 split_strategy.split_tree(
379 peano_tree=tree,
380 decomposition=decomposition,
381 traversal="bfs_finest_first",
382 enable_propagate_bottom_up=True,
383 enable_optimize_hierarchy=True,
384 )
385
386 # Root stays 0
387 assert tree._tree_arrays[0][0][0] == (1, 0)
388
389 # Level-1 promotions after Stage III
390 expected_l1 = [
391 [0, 3, 2],
392 [0, 1, 3],
393 [0, 1, 3],
394 ]
395 for a in range(3):
396 for b in range(3):
397 assert tree._tree_arrays[1][a][b] == (1, expected_l1[a][b])
398
399 # Stage III changes only non-leaf ownership; leaf split remains unchanged.
400 tree.update_split_pattern()
401 assert list(tree._split_pattern) == [147.0, 146.0, 146.0, 146.0]
test_tree3_all_rows_stage1_leaf_distribution()
Stage I test for the all-rows representation variant.
test_tree3_all_rows_stage3_promotes_level1_not_root()
Stage III test for the all-rows representation variant.
test_tree3_stage1_bfs_finest_first_leaf_distribution()
Integration test for Stage I with traversal="bfs_finest_first".
test_tree3_stage2_bottom_up_propagation()
Integration test for Stage I + Stage II with traversal="bfs_finest_first".
test_tree3_stage3_no_root_promotion_with_single_root_first_row_embedding()
Integration test for Stage I + Stage II + Stage III (full pipeline).
test_tree3_all_rows_stage2_bottom_up_propagation()
Stage II test for the all-rows representation variant.