diff --git a/src/SimpleGraphs/SimpleGraphs.jl b/src/SimpleGraphs/SimpleGraphs.jl index 1a75a24d9..c1e664682 100644 --- a/src/SimpleGraphs/SimpleGraphs.jl +++ b/src/SimpleGraphs/SimpleGraphs.jl @@ -91,6 +91,7 @@ export AbstractSimpleGraph, cycle_digraph, binary_tree, double_binary_tree, + regular_tree, roach_graph, clique_graph, barbell_graph, diff --git a/src/SimpleGraphs/generators/staticgraphs.jl b/src/SimpleGraphs/generators/staticgraphs.jl index 64e004877..251499294 100644 --- a/src/SimpleGraphs/generators/staticgraphs.jl +++ b/src/SimpleGraphs/generators/staticgraphs.jl @@ -536,6 +536,62 @@ function double_binary_tree(k::Integer) return g end +""" + regular_tree([T::Type], 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. +The optional `T` argument specifies the element type, which defaults to `Int64`. + +# Examples +```jldoctest +julia> regular_tree(4, 3) +{40, 39} undirected simple Int64 graph + +julia> regular_tree(Int8, 3, 2) +{7, 6} undirected simple Int8 graph + +julia> regular_tree(5, 2) == binary_tree(5) +true +``` +""" +function regular_tree(T::Type{<:Integer}, k::Integer, z::Integer) + z <= 0 && throw(DomainError(z, "number of children must be positive")) + z == 1 && return path_graph(T(k)) + k <= 0 && return SimpleGraph(zero(T)) + k == 1 && return SimpleGraph(one(T)) + nbig = (BigInt(z)^k - 1) ÷ (z - 1) + if Graphs.isbounded(k) && nbig > typemax(T) + throw(InexactError(:convert, T, nbig)) + end + + n = T(nbig) + ne = n - 1 + fadjlist = Vector{Vector{T}}(undef, n) + @inbounds fadjlist[1] = convert.(T, 2:(z + 1)) + @inbounds for l in 2:(k - 1) + w = (z^(l - 1) - 1) ÷ (z - 1) + x = w + z^(l - 1) + @simd for i in 1:(z^(l - 1)) + j = w + i + fadjlist[j] = [ + T(ceil((j - x) / z) + w) + convert.(T, (x + (i - 1) * z + 1):(x + i * z)) + ] + end + end + l = k + w = (z^(l - 1) - 1) ÷ (z - 1) + x = w + z^(l - 1) + @inbounds @simd for j in (w + 1):x + fadjlist[j] = T[ceil((j - x) / z) + w] + end + return SimpleGraph(ne, fadjlist) +end + +regular_tree(k::Integer, z::Integer) = regular_tree(Int64, k, z) + """ roach_graph(k) diff --git a/test/simplegraphs/generators/staticgraphs.jl b/test/simplegraphs/generators/staticgraphs.jl index 17107adce..b1e7c9cf4 100644 --- a/test/simplegraphs/generators/staticgraphs.jl +++ b/test/simplegraphs/generators/staticgraphs.jl @@ -411,6 +411,23 @@ @test isvalid_simplegraph(g) end + @testset "Regular Trees" begin + g = @inferred(regular_tree(3, 3)) + I = [1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + J = [2, 3, 4, 1, 5, 6, 7, 8, 1, 9, 10, 1, 11, 12, 13, 2, 2, 2, 3, 3, 3, 4, 4, 4] + V = ones(Int, length(I)) + Adj = sparse(I, J, V) + @test Adj == sparse(g) + @test isvalid_simplegraph(g) + @test_throws InexactError regular_tree(Int8, 4, 5) + g = @inferred(regular_tree(Int16, 4, 5)) + @test isvalid_simplegraph(g) + # test that z = 1 recovers a path graph + @test all(regular_tree(k, 1) == path_graph(k) for k in 0:10) + # test that z = 2 recovers a binary tree + @test all(regular_tree(k, 2) == binary_tree(k) for k in 0:10) + end + @testset "Roach Graphs" begin rg3 = @inferred(roach_graph(3)) # [3]