Skip to content

Add regular tree generator#197

Merged
Krastanov merged 13 commits intoJuliaGraphs:masterfrom
stecrotti:master
Feb 21, 2026
Merged

Add regular tree generator#197
Krastanov merged 13 commits intoJuliaGraphs:masterfrom
stecrotti:master

Conversation

@stecrotti
Copy link
Copy Markdown
Contributor

@stecrotti stecrotti commented Nov 23, 2022

Generator for a regular tree, also known as Bethe Lattice or Cayley Tree, which generalizes a binary tree to arbitrary degree.

I tried to keep the code as close as possible to the one for binary_tree.

@stecrotti stecrotti marked this pull request as ready for review November 23, 2022 15:28
@codecov
Copy link
Copy Markdown

codecov bot commented Nov 24, 2022

Codecov Report

❌ Patch coverage is 92.85714% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.28%. Comparing base (82fee84) to head (328d8df).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/SimpleGraphs/generators/staticgraphs.jl 92.85% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #197      +/-   ##
==========================================
- Coverage   97.30%   97.28%   -0.02%     
==========================================
  Files         123      123              
  Lines        7378     7406      +28     
==========================================
+ Hits         7179     7205      +26     
- Misses        199      201       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@stecrotti stecrotti marked this pull request as draft November 24, 2022 12:52
Comment thread src/SimpleGraphs/generators/staticgraphs.jl Outdated
Comment on lines +556 to +557
k <= 0 && return SimpleGraph(0)
k == 1 && return SimpleGraph(1)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In these cases we return a graph of of eltype Int, but in all other cases we return a graph of type T.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be completely honest, I am not sure if it is the best idea to determine the eltype of the graphs from k and z - we do that indeed for other graphs here, but I never really liked that.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can have a function signature

regular_tree(T::Type{<:Integer}, k::Integer, z::Integer)

and then define something like

regular_tree(k::Integer, z::Integer) = regular_tree(Int64, k, z)

that for be similar to the function sprand from SparseArrays for example.

"""
regular_tree(k::Integer, z::integer)

Create a [regular tree](https://en.wikipedia.org/wiki/Bethe_lattice),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wikipedia article might be a bit confusing, because it talks about infinite trees in the introduction.

Maybe we can decribe that a bit more clearly here?

If I understood correctly, what you want to create is a perfect k-ary tree such as described here? https://en.wikipedia.org/wiki/M-ary_tree#Types_of_m-ary_trees

@stecrotti
Copy link
Copy Markdown
Contributor Author

By the way I called it 'regular tree' because of the analogy with regular graphs but that might not be too accurate because:

  1. A regular tree is not a regular graph (because of the leaves)
  2. z-regular graphs have nodes of degree z while z-ary trees have nodes of degree z+1, potentially confusing

That said, mary_tree(k, m), zary_tree(k, z) don't look great to me.

@stecrotti
Copy link
Copy Markdown
Contributor Author

Changes:

  • New method with type regular_tree(T::Type{<:Integer}, k::Integer, z::Integer)
  • Improved docs with correct wiki page
  • InexactError instead of DomainError when number of vertices is not representable
  • Assert z > 0, fallback to a path graph in case z = 1
  • Appropriate conversions to the provided type T, checked by test

@stecrotti stecrotti marked this pull request as ready for review November 30, 2022 09:47
@simonschoelly
Copy link
Copy Markdown
Member

By the way I called it 'regular tree' because of the analogy with regular graphs but that might not be too accurate because:

1. A regular tree is not a regular graph (because of the leaves)

2. z-regular graphs have nodes of degree z while z-ary trees have nodes of degree z+1, potentially confusing

That said, mary_tree(k, m), zary_tree(k, z) don't look great to me.

I think regular tree is perfectly fine. For example here, the same term is used: https://www.wolframalpha.com/examples/mathematics/discrete-mathematics/graph-theory/regular-trees/

Copy link
Copy Markdown
Member

@aurorarossi aurorarossi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments on the docstrings. If you agree with the style then I will open a PR to fix double_binary_tree and binary_tree

end

"""
regular_tree([T=Int64, ], k::Integer, z::integer)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except for binary_tree and double_binary_tree all the other functions of this file have the following style:

Suggested change
regular_tree([T=Int64, ], k::Integer, z::integer)
regular_tree(T, k, z)

Comment on lines +548 to +550
julia> regular_tree(4, 3)
{40, 39} undirected simple Int64 graph

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove it and I would put it in the docstring of the function regular_tree(k,z).

Suggested change
julia> regular_tree(4, 3)
{40, 39} undirected simple Int64 graph

Comment on lines +553 to +555

julia> regular_tree(5, 2) == binary_tree(5)
true
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

Suggested change
julia> regular_tree(5, 2) == binary_tree(5)
true

end
return SimpleGraph(ne, fadjlist)
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add the docstring also to this function.

Suggested change
"""
regular_tree(k, z)
Create a regular tree or [perfect z-ary tree](https://en.wikipedia.org/wiki/M-ary_tree#Types_of_m-ary_trees):
a `k`-level tree where all nodes except the leaves have exactly `z` children.
For `z = 2` one recovers a binary tree.
# Examples
```jldoctest
julia> regular_tree(4, 3)
{40, 39} undirected simple Int64 graph
julia> regular_tree(5, 2) == binary_tree(5)
true
```
"""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's quite often the case, that the docstrings for multiple methods of a function are only added for a single method - I think that would also be better here, than adding docstrings for both methods of this function.

@dapias
Copy link
Copy Markdown

dapias commented Dec 14, 2022

I just realized that a $k$-ary tree is not the same as a Cayley tree of connectivity $k$ because in a $k$-ary tree, the root has connectivity $k$, and all the nodes excepting those in the leaves have connectivity $k+1$. Look, for instance, at the attached plot, nodes 2,3 and 4 have connectivity four and not three as it would be the case for a Cayley tree

Bildschirm­foto 2022-12-14 um 09 31 30

@stecrotti
Copy link
Copy Markdown
Contributor Author

Agree! Indeed it was corrected and should be ok in the current version of this PR

@dapias
Copy link
Copy Markdown

dapias commented Dec 15, 2022

Is there a function to create an actual Cayley tree?

@stecrotti
Copy link
Copy Markdown
Contributor Author

Not that i know of

@dapias
Copy link
Copy Markdown

dapias commented Dec 16, 2022

I am not an expert, but it seems trivial to be implemented, given the actual code for the tree. After setting up the central node (root), one only needs to demand that each child has $(z-1)$ children instead of $z$

```
"""
function regular_tree(T::Type{<:Integer}, k::Integer, z::Integer)
z <= 0 && throw(DomainError(z, "number of children must be positive"))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we also check for k >= 0 here?

Comment on lines +563 to +567
if Graphs.isbounded(k) && (BigInt(z)^k - 1) ÷ (z - 1) > typemax(T)
throw(InexactError(:convert, T, (BigInt(z)^k - 1) ÷ (z - 1)))
end

n = T((z^k - 1) / (z - 1))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already calculate the number of vertices with (BigInt(z)^k - 1) ÷ (z - 1), so we we could just reuse this for n.

With the expression n = T((z^k - 1) / (z - 1)) there are two issues:

  • z^k might overflow before converting it to T
  • By using the / operator instead of ÷ your intermediate result will be a double

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of these overflow issues probably won't happen if we restrict k and z to Int in the function definition.

@stecrotti
Copy link
Copy Markdown
Contributor Author

Changed the signature to match the rest of the file as suggested by @aurorarossi, but kept only one docstring.

Fixed potential overflow as suggested by @simonschoelly.

@simonschoelly
Copy link
Copy Markdown
Member

There is this one comment from me, about k >= 0 - not sure if you have seen it, but feel free to ignore that it you think we should not change anything there.

Otherwise looks good for me - I did not check the math, but choose to believe the tests.

@stecrotti
Copy link
Copy Markdown
Contributor Author

Sure, i replied under your review comment:

I stuck to what was used in the rest of the file which is that for k<=0 it returns a SimpleGraph(zero(T)). I guess there was a good reason for that? If not it makes total sense to error if k<0.

@gdalle gdalle added the enhancement New feature or request label Jun 16, 2023
@Krastanov
Copy link
Copy Markdown
Member

@stecrotti , it seems this PR lost steam a few years ago. Could you run the formatter on it -- after that it should be good to go for merging.

@Krastanov
Copy link
Copy Markdown
Member

doctests are actually failing, so for now I will mark this as a draft to manage my review queue a bit more easily -- do not hesitate to convert it back to not draft if you decide to look into it

@Krastanov Krastanov marked this pull request as draft February 21, 2026 05:36
@stecrotti
Copy link
Copy Markdown
Contributor Author

I fixed the doctests.
Tests are failing on pre-releases which i understand is a known issue.
Coverage fails are pointing to lines of code consisting of only end, so i guess that's ok too.

@stecrotti stecrotti marked this pull request as ready for review February 21, 2026 12:41
@github-actions
Copy link
Copy Markdown
Contributor

Benchmark Results (Julia v1)

Time benchmarks
master 328d8df... master / 328d8df...
centrality/digraphs/betweenness_centrality 15.7 ± 0.4 ms 15.8 ± 0.4 ms 0.995 ± 0.036
centrality/digraphs/closeness_centrality 10.9 ± 0.41 ms 11 ± 0.4 ms 0.99 ± 0.052
centrality/digraphs/degree_centrality 1.96 ± 0.16 μs 1.91 ± 0.14 μs 1.03 ± 0.11
centrality/digraphs/katz_centrality 0.853 ± 0.059 ms 0.846 ± 0.061 ms 1.01 ± 0.1
centrality/digraphs/pagerank 0.0361 ± 0.00055 ms 0.036 ± 0.00055 ms 1 ± 0.022
centrality/graphs/betweenness_centrality 28.6 ± 1.1 ms 28.5 ± 1.1 ms 1 ± 0.053
centrality/graphs/closeness_centrality 21 ± 0.11 ms 20.9 ± 0.11 ms 1 ± 0.0074
centrality/graphs/degree_centrality 1.47 ± 0.14 μs 1.46 ± 0.15 μs 1.01 ± 0.14
centrality/graphs/katz_centrality 1 ± 0.056 ms 1 ± 0.066 ms 1 ± 0.086
connectivity/digraphs/strongly_connected_components 0.0428 ± 0.0012 ms 0.0431 ± 0.0012 ms 0.993 ± 0.04
connectivity/graphs/connected_components 22.5 ± 0.75 μs 24 ± 0.69 μs 0.938 ± 0.041
core/edges/digraphs 7.02 ± 0.011 μs 7.02 ± 0.011 μs 1 ± 0.0022
core/edges/graphs 17.2 ± 0.13 μs 17 ± 0.1 μs 1.01 ± 0.0097
core/has_edge/digraphs 5.16 ± 0.36 μs 5.14 ± 0.35 μs 1 ± 0.098
core/has_edge/graphs 5.61 ± 0.36 μs 5.49 ± 0.36 μs 1.02 ± 0.094
core/nv/digraphs 0.37 ± 0.01 μs 0.361 ± 0.01 μs 1.02 ± 0.04
core/nv/graphs 0.381 ± 0.011 μs 0.381 ± 0.01 μs 1 ± 0.039
edges/fille 7.63 ± 0.88 μs 7.71 ± 1.1 μs 0.991 ± 0.18
edges/fillp 4.73 ± 3.6 μs 4.72 ± 3.8 μs 1 ± 1.1
edges/tsume 2.54 ± 0.03 μs 2.52 ± 0.02 μs 1.01 ± 0.014
edges/tsump 2.52 ± 0.031 μs 2.5 ± 0.049 μs 1.01 ± 0.023
insertions/SG(n,e) Generation 24.7 ± 3.2 ms 24.8 ± 3.7 ms 0.994 ± 0.2
parallel/egonet/twohop 0.289 ± 0.0019 s 0.271 ± 0.0027 s 1.07 ± 0.013
parallel/egonet/vertexfunction 2.25 ± 0.082 ms 2.09 ± 0.065 ms 1.08 ± 0.051
serial/egonet/twohop 0.287 ± 0.0032 s 0.272 ± 0.0012 s 1.06 ± 0.013
serial/egonet/vertexfunction 2.22 ± 0.048 ms 2.02 ± 0.047 ms 1.1 ± 0.035
traversals/digraphs/bfs_tree 0.0491 ± 0.0045 ms 0.049 ± 0.0047 ms 1 ± 0.13
traversals/digraphs/dfs_tree 0.0629 ± 0.0082 ms 0.0636 ± 0.0084 ms 0.99 ± 0.18
traversals/graphs/bfs_tree 0.0526 ± 0.0019 ms 0.0526 ± 0.0024 ms 0.999 ± 0.058
traversals/graphs/dfs_tree 0.065 ± 0.0026 ms 0.0647 ± 0.0024 ms 1.01 ± 0.055
time_to_load 0.515 ± 0.0018 s 0.516 ± 0.00013 s 0.999 ± 0.0035
Memory benchmarks
master 328d8df... master / 328d8df...
centrality/digraphs/betweenness_centrality 0.29 M allocs: 24 MB 0.29 M allocs: 24 MB 1
centrality/digraphs/closeness_centrality 18.6 k allocs: 14.5 MB 18.6 k allocs: 14.5 MB 1
centrality/digraphs/degree_centrality 8 allocs: 5.01 kB 8 allocs: 5.01 kB 1
centrality/digraphs/katz_centrality 2.63 k allocs: 2.83 MB 2.63 k allocs: 2.83 MB 1
centrality/digraphs/pagerank 21 allocs: 14.9 kB 21 allocs: 14.9 kB 1
centrality/graphs/betweenness_centrality 0.545 M allocs: 0.0313 GB 0.545 M allocs: 0.0313 GB 1
centrality/graphs/closeness_centrality 19.3 k allocs: 14 MB 19.3 k allocs: 14 MB 1
centrality/graphs/degree_centrality 10 allocs: 5.43 kB 10 allocs: 5.43 kB 1
centrality/graphs/katz_centrality 2.96 k allocs: 3.1 MB 2.96 k allocs: 3.1 MB 1
connectivity/digraphs/strongly_connected_components 1.05 k allocs: 0.075 MB 1.05 k allocs: 0.075 MB 1
connectivity/graphs/connected_components 0.061 k allocs: 22.5 kB 0.061 k allocs: 22.5 kB 1
core/edges/digraphs 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
core/edges/graphs 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
core/has_edge/digraphs 20 allocs: 12.6 kB 20 allocs: 12.6 kB 1
core/has_edge/graphs 28 allocs: 13.8 kB 28 allocs: 13.8 kB 1
core/nv/digraphs 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
core/nv/graphs 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
edges/fille 3 allocs: 0.153 MB 3 allocs: 0.153 MB 1
edges/fillp 3 allocs: 0.153 MB 3 allocs: 0.153 MB 1
edges/tsume 0 allocs: 0 B 0 allocs: 0 B
edges/tsump 0 allocs: 0 B 0 allocs: 0 B
insertions/SG(n,e) Generation 0.0465 M allocs: 10.9 MB 0.0465 M allocs: 11 MB 0.998
parallel/egonet/twohop 10 allocs: 0.0768 MB 10 allocs: 0.0768 MB 1
parallel/egonet/vertexfunction 10 allocs: 0.0768 MB 10 allocs: 0.0768 MB 1
serial/egonet/twohop 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
serial/egonet/vertexfunction 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
traversals/digraphs/bfs_tree 2.34 k allocs: 0.113 MB 2.34 k allocs: 0.113 MB 1
traversals/digraphs/dfs_tree 2.44 k allocs: 0.118 MB 2.44 k allocs: 0.118 MB 1
traversals/graphs/bfs_tree 2.52 k allocs: 0.121 MB 2.52 k allocs: 0.121 MB 1
traversals/graphs/dfs_tree 2.63 k allocs: 0.127 MB 2.63 k allocs: 0.127 MB 1
time_to_load 0.149 k allocs: 11.1 kB 0.145 k allocs: 11 kB 1.02

@Krastanov
Copy link
Copy Markdown
Member

Thank you, Stefano! Your quick turnaround is extremely highly appreciated!

You might have noticed that we are struggling to have enough volunteers on the team. Let me know if you are interested in being added as a maintainer -- help with reviews can really help revive this ecosystem.

@Krastanov Krastanov merged commit 98942df into JuliaGraphs:master Feb 21, 2026
11 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants