Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 5, 2026

The splits_to_edge() function had a hard-coded 2048 leaf limit to prevent stack overflow from fixed-size arrays. This prevented conversion of large phylogenetic trees.

Changes

src/splits_to_tree.cpp

  • Removed 2048 leaf check
  • Implemented hybrid allocation:
    • Stack allocation (aligned, fast) for trees ≤2048 tips
    • Heap allocation (std::vector) for larger trees
  • Updated insert_ancestor() signature to accept raw pointers
  • Changed split_order to heap allocation for consistency

tests/testthat/test-Splits.R

  • Updated test to verify 3000-leaf trees work correctly

Example

# Previously failed with "This many leaves are not (yet) supported"
tree <- BalancedTree(3000)
splits <- as.Splits(tree)
result <- as.phylo(splits)  # Now works

Trees at the boundary (2048 tips) continue using stack allocation for performance, while larger trees seamlessly use heap allocation.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • notA-file.run1.t
    • Triggering command: /opt/R/4.5.2/lib/R/bin/exec/R /opt/R/4.5.2/lib/R/bin/exec/R --vanilla --quiet -e library(testthat);~&#43;~library(TreeTools);~&#43;~test_dir(&#39;tests/testthat&#39;,~&#43;~reporter~&#43;~=~&#43;~&#39;summary&#39;) /R/include /home/REDACTED/wor/usr/local/include ndor/bin/as /home/REDACTED/worsh de /home/REDACTED/wor-- git add /R/include -v Rcpp/include /home/REDACTED/worrm 8933f853bc&#39; SHLI-Rf /home/REDACTED/wor/tmp/RtmpnNK0GU sh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Support >2048 leaves in splits_to_edge()</issue_title>
<issue_description>Hard-coded limit in splits_to_tree.cpp now guards against stack overflow.

We need to provide an on-heap alternative to

  alignas(64) std::array<int16, SL_MAX_TIPS + SL_MAX_SPLITS> parent{};
  alignas(64) std::array<int16, SL_MAX_TIPS> patriarch{};

</issue_description>

<agent_instructions>We want splits_to_edge to handle arbitrarily-large trees, whilst retaining high performance for smaller trees.
We've just removed the hard limit on the size of SplitList, in SplitList.h.

test_that("as.phylo.Splits() fails gracefully", {
expect_error(
as.phylo(as.Splits(BalancedTree(3000))),
"many leaves are not .*supported"
)
})

fails, if we remove our check for tree size, with a seg-fault.

Please update the test case also to demonstrate success rather than error, and run the tests to make sure that your proposed code succeeds and does not introduce errors.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 3 commits January 5, 2026 16:49
Copilot AI changed the title [WIP] Support arbitrarily large trees in splits_to_edge() Remove 2048 leaf limit in splits_to_edge() with hybrid stack/heap allocation Jan 5, 2026
Copilot AI requested a review from ms609 January 5, 2026 16:59
@github-actions
Copy link

github-actions bot commented Jan 5, 2026

Performance benchmark results

Call Status Change Time (ms)
as.Splits(bigTrees) ⚪ NSD -6.32% 21.4 →
22, 23.1
as.Splits(someTrees) ⚪ NSD -2.36% 11.1 →
11.3, 11.5
Consensus(forest1k.888, check = FALSE) ⚪ NSD -0.06% 95.6 →
96.1, 95.7
Consensus(forest201.80, check = FALSE) ⚪ NSD -1.06% 3.97 →
3.98, 4.05
Consensus(forest21.260, 0.5, FALSE) ⚪ NSD -0.49% 1.2 →
1.2, 1.2
Consensus(forest21.260) ⚪ NSD -0.66% 1.19 →
1.19, 1.2
Consensus(forestMaj, 0.5, FALSE) ⚪ NSD -0.62% 2.92 →
2.93, 2.95
DropTip(tr2000, 5) ⚪ NSD 0.89% 20.5 →
20, 20.5
DropTip(tr80, 5) ⚪ NSD -0.06% 0.105 →
0.104, 0.106
DropTip(unlen2k, 5) ⚪ NSD 0.56% 0.216 →
0.213, 0.217
DropTip(unlen80, 5) ⚪ NSD -0.74% 0.0405 →
0.0406, 0.0409
lapply(bigSplits, as.phylo) ⚪ NSD -0.5% 30.4 →
30.4, 31.3
lapply(someSplits, as.phylo) ⚪ NSD -2.32% 14.1 →
14.2, 14.6
PathLengths(tr2000, full = TRUE) 🟣 ~same 2.89% 19.9 →
19.1, 19.3
PathLengths(tr80, full = TRUE) ⚪ NSD -0.41% 0.102 →
0.104, 0.102
PathLengths(tr80Unif, full = TRUE) ⚪ NSD 69.2% 0.104 →
0.0315, 0.104
RootTree(tr2000, 5) ⚪ NSD 2.49% 0.397 →
0.38, 0.396
RootTree(tr80, c("t3", "t36")) 🟣 ~same 5.73% 0.0742 →
0.0703, 0.0694
RootTree(tr80, "t3") ⚪ NSD 4.13% 0.0521 →
0.0497, 0.0502
RootTree(tr80, "t30") 🟣 ~same 4.77% 0.0527 →
0.0499, 0.0505
RootTree(unlen2k, 5) ⚪ NSD 0.64% 0.332 →
0.331, 0.326
RootTree(unlen80, c("t3", "t36")) ⚪ NSD 4.56% 0.0678 →
0.0648, 0.0647
RootTree(unlen80, "t3") 🟣 ~same 5.55% 0.0453 →
0.0431, 0.0425
RootTree(unlen80, "t30") 🟣 ~same 5.13% 0.0459 →
0.0436, 0.0435
TreeDist::RobinsonFoulds(forest201.80) ⚪ NSD -1.49% 15.3 →
15.6, 15.7
TreeDist::RobinsonFoulds(forest21.888) ⚪ NSD 1.4% 3.24 →
3.18, 3.2
TreeTools:::path_lengths(tr80$edge, tr80$edge.length, FALSE) ⚪ NSD -0.23% 0.0909 →
0.0914, 0.0908
TreeTools:::postorder_order(bal40) ⚪ NSD -0.61% 0.00165 →
0.00166, 0.00166
TreeTools:::postorder_order(bal40k) ⚪ NSD -0.45% 0.445 →
0.447, 0.446
TreeTools:::postorder_order(dbal40) ⚪ NSD 4.93% 0.00182 →
0.00174, 0.00172
TreeTools:::postorder_order(dbal40k) 🟢 Faster! 11.91% 2.37 →
2.09, 2.09
TreeTools:::postorder_order(dpec40) 🟢 Faster! 15.77% 0.00299 →
0.00252, 0.00251
TreeTools:::postorder_order(dpec40k) 🟢 Faster! 6.29% 3610 →
3390, 3380
TreeTools:::postorder_order(drnd80) 🟢 Faster! 19.98% 0.00502 →
0.00402, 0.00402
TreeTools:::postorder_order(nbal40) ⚪ NSD -0.47% 0.0021 →
0.00211, 0.00211
TreeTools:::postorder_order(nbal40k) 🟢 Faster! 7.33% 2.43 →
2.23, 2.27
TreeTools:::postorder_order(npec40) 🟢 Faster! 14.29% 0.0033 →
0.00283, 0.00282
TreeTools:::postorder_order(npec40k) 🟢 Faster! 6.08% 3630 →
3420, 3410
TreeTools:::postorder_order(nrnd80) 🟢 Faster! 18.57% 0.00561 →
0.00458, 0.00456
TreeTools:::postorder_order(pec40) ⚪ NSD 0.55% 0.00164 →
0.00162, 0.00164
TreeTools:::postorder_order(pec40k) ⚪ NSD -0.56% 0.427 →
0.427, 0.432
TreeTools:::postorder_order(rnd80) ⚪ NSD -0.48% 0.00207 →
0.00208, 0.00209

@ms609 ms609 marked this pull request as ready for review January 6, 2026 07:29
@ms609 ms609 merged commit d80b95c into large-trees Jan 6, 2026
4 checks passed
@ms609 ms609 deleted the copilot/support-arbitrarily-large-trees branch January 6, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants