From aa4a4fb1bd2ec92a26118b80f0ae00610b647cb5 Mon Sep 17 00:00:00 2001 From: Pavel Dydyshka Date: Mon, 21 Oct 2024 01:34:25 +0200 Subject: [PATCH 1/6] Add algos for distance-regular graphs --- src/Graphs.jl | 9 +- src/distance_regular/distance_regular.jl | 183 ++++++++++++++++++++++ test/distance_regular/distance_regular.jl | 11 ++ test/runtests.jl | 1 + 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 src/distance_regular/distance_regular.jl create mode 100644 test/distance_regular/distance_regular.jl diff --git a/src/Graphs.jl b/src/Graphs.jl index 86bc0946a..dae3ccbe7 100644 --- a/src/Graphs.jl +++ b/src/Graphs.jl @@ -437,7 +437,13 @@ export vertex_cover, # longestpaths - dag_longest_path + dag_longest_path, + + # distance-regular + is_distance_regular, + intersection_array, + global_parameters, + is_strongly_regular """ Graphs @@ -490,6 +496,7 @@ const Edge = Graphs.SimpleGraphs.SimpleEdge include("degeneracy.jl") include("digraph/transitivity.jl") +include("distance_regular/distance_regular.jl") include("cycles/johnson.jl") include("cycles/hawick-james.jl") include("cycles/karp.jl") diff --git a/src/distance_regular/distance_regular.jl b/src/distance_regular/distance_regular.jl new file mode 100644 index 000000000..1f3581fcb --- /dev/null +++ b/src/distance_regular/distance_regular.jl @@ -0,0 +1,183 @@ +""" + is_distance_regular(G::AbstractGraph) -> Bool + +Return `true` if graph `G` is distance regular, `false` otherwise. + +A connected graph ``G`` is distance-regular if for any nodes ``x,y`` +and any integers ``i, j= 0, …, d`` (where ``d`` is the graph +diameter), the number of vertices at distance ``i`` from ``x`` and +distance ``j`` from ``y`` depends only on ``i,j`` and the graph distance +between ``x`` and ``y``, independently of the choice of ``x`` and ``y``. + +# Examples +```jldoctest +julia> G = smallgraph(:icosahedral); + +julia> is_distance_regular(G) +true +``` + +See Also: [`intersection_array`](@ref), [`global_parameters`](@ref) + +# References +1. Brouwer, A. E.; Cohen, A. M.; and Neumaier, A. + Distance-Regular Graphs. New York: Springer-Verlag, 1989. +2. Weisstein, Eric W. "Distance-Regular Graph." + http://mathworld.wolfram.com/Distance-RegularGraph.html +""" +function is_distance_regular(G::AbstractGraph) + isgood, _ = _intersection_array(G; check=true) + return isgood +end + + +""" + intersection_array(G::AbstractGraph) -> (b, c) + +Return the intersection array of a distance-regular graph `G`. + +Given a distance-regular graph G with integers ``b_i, c_i, i = 0, …, d`` +such that for any 2 vertices ``x, y`` in ``G`` at a distance ``i = d(x, y)``, +there are exactly ``c_i`` neighbors of ``y`` at a distance of ``i-1`` from +``x`` and ``b_i`` neighbors of ``y`` at a distance of ``i+1`` from ``x``. + +A distance regular graph's intersection array is given by +```math +\\{b_0, b_1, …, b_{d-1}; c_1, c_2, …, c_d\\} +``` + +# Examples +```jldoctest +julia> G = smallgraph(:icosahedral); + +julia> intersection_array(G) +([5, 2, 1], [1, 2, 5]) +``` +""" +function intersection_array(G::AbstractGraph; check::Bool=true) + isgood, int_arr = _intersection_array(G; check=check) + check && !isgood && throw(ArgumentError("graph is not distance regular.")) + return int_arr +end + + +function _intersection_array(G::AbstractGraph; check::Bool=true) + check && !allequal(degree(G)) && isempty(vertices(G)) && + is_connected(G) && return (false, (Int[], Int[])) + paths_matrix = mapreduce(hcat, vertices(G)) do vertex + dijkstra_shortest_paths(G, vertex).dists + end + diameter = maximum(paths_matrix) + bv = zeros(Int, diameter+1) # b intersection array + cv = copy(bv) # c intersection array + for u in vertices(G), v in vertices(G) + i = paths_matrix[u, v] + # number of neighbors of v at a distance of i-1 from u + c = count(neighbors(G, v)) do n + paths_matrix[n, u] == i - 1 + end + # number of neighbors of v at a distance of i+1 from u + b = count(neighbors(G, v)) do n + paths_matrix[n, u] == i + 1 + end + # b and c cannot be zero + # hence if any of bv[i+1] or cv[i+1] + # is not zero nor corresponding b, c + # the graph is not distance-regular + if check && (bv[i+1] != 0 || bv[i+1] != b || cv[i+1] != 0 || cv[i+1] != c) + return (false, (Int[], Int[])) + end + bv[i+1] = b + cv[i+1] = c + end + pop!(bv); popfirst!(cv) + return (true, (bv, cv)) +end + + +""" + global_parameters(b, c) + +Returns global parameters for a given intersection array `b, c`. + +Given a distance-regular graph ``G`` with integers ``b_i, c_i, i = 0, …, d`` +such that for any 2 vertices ``x,y`` in ``G`` at a distance ``i = d(x,y)``, there +are exactly ``c_i`` neighbors of ``y`` at a distance of ``i-1`` from ``x`` and +``b_i`` neighbors of ``y`` at a distance of ``i+1`` from ``x``. + +Thus, a distance regular graph has the global parameters, +```math +[[c_0, a_0, b_0], [c_1, a_1, b_1], …, [c_d, a_d, b_d]] +``` +for the intersection array +```math +[b_0, b_1, …, b_{d-1}; c_1, c_2, …, c_d] +```` +where ``a_i + b_i + c_i = k`` , ``k`` is the degree of every vertex. + +# Returns +iterable + An iterable over three tuples. + +# Examples +```jldoctest +julia> G = smallgraph(:dodecahedral); + +julia> b, c = intersection_array(G); + +julia> collect(global_parameters(b, c)) +[(0, 0, 3), (1, 0, 2), (1, 1, 1), (1, 1, 1), (2, 0, 1), (3, 0, 0)] +``` +# References +.. [1] Weisstein, Eric W. "Global Parameters." + From MathWorld--A Wolfram Web Resource. + http://mathworld.wolfram.com/GlobalParameters.html + +See Also [`intersection_array`](@ref) +""" +function global_parameters(b::AbstractVector{<:Integer}, c::AbstractVector{<:Integer}) + return ((y, b[begin] - x - y, x) for (x, y) in zip([b; 0], [0; c])) +end + + +""" + global_parameters(G; check=false) + +Returns global parameters for a given distance-regular graph G. +""" +function global_parameters(G::AbstractGraph; check::Bool=true) + return global_parameters(intersection_array(G; check=check)...) +end + + +""" + is_strongly_regular(G) + +Returns `true` if and only if the given graph `G` is strongly regular. + +An undirected graph is *strongly regular* if + +* it is regular, +* each pair of adjacent vertices has the same number of neighbors in common, +* each pair of nonadjacent vertices has the same number of neighbors in common. + +Each strongly regular graph is a distance-regular graph. +Conversely, if a distance-regular graph has diameter two, then it is +a strongly regular graph. For more information on distance-regular +graphs, see [`is_distance_regular`](@ref). + +# Examples + +The cycle graph on five vertices is strongly regular. It is +two-regular, each pair of adjacent vertices has no shared neighbors, +and each pair of nonadjacent vertices has one shared neighbor: + +```jldoctest +julia> G = cycle_graph(5); + +julia> is_strongly_regular(G) +true +``` +""" +is_strongly_regular(G::AbstractGraph) = is_distance_regular(G) && diameter(G) == 2 + diff --git a/test/distance_regular/distance_regular.jl b/test/distance_regular/distance_regular.jl new file mode 100644 index 000000000..88bda44a3 --- /dev/null +++ b/test/distance_regular/distance_regular.jl @@ -0,0 +1,11 @@ +@testset "Distance-regular" begin + icosahedral = smallgraph(:icosahedral) + dodecahedral = smallgraph(:dodecahedral) + @testset "Is distance-regular" begin + @test is_distance_regular(icosahedral) + @test is_distance_regular(dodecahedral) + end + @testset "Intersection array" begin + @test intersection_array(icosahedral) == ([5, 2, 1], [1, 2, 5]) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 83644faff..b73e6cf21 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -82,6 +82,7 @@ tests = [ "degeneracy", "distance", "digraph/transitivity", + "distance_regular/distance_regular", "cycles/hawick-james", "cycles/johnson", "cycles/karp", From c70a846136b0fb2f1980e99b372944b5ab2c3fcb Mon Sep 17 00:00:00 2001 From: Pavel Dydyshka Date: Mon, 21 Oct 2024 09:57:55 +0200 Subject: [PATCH 2/6] Fix logical expr in _intersection_array --- src/distance_regular/distance_regular.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distance_regular/distance_regular.jl b/src/distance_regular/distance_regular.jl index 1f3581fcb..18984c8e2 100644 --- a/src/distance_regular/distance_regular.jl +++ b/src/distance_regular/distance_regular.jl @@ -84,7 +84,7 @@ function _intersection_array(G::AbstractGraph; check::Bool=true) # hence if any of bv[i+1] or cv[i+1] # is not zero nor corresponding b, c # the graph is not distance-regular - if check && (bv[i+1] != 0 || bv[i+1] != b || cv[i+1] != 0 || cv[i+1] != c) + if check && (bv[i+1] != 0 && bv[i+1] != b || cv[i+1] != 0 && cv[i+1] != c) return (false, (Int[], Int[])) end bv[i+1] = b From 09b26bee8adb5347735a51b0066d26b041547c2d Mon Sep 17 00:00:00 2001 From: Pavel Dydyshka Date: Mon, 21 Oct 2024 10:34:23 +0200 Subject: [PATCH 3/6] Fix check logical expr in _intersection_array --- src/distance_regular/distance_regular.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/distance_regular/distance_regular.jl b/src/distance_regular/distance_regular.jl index 18984c8e2..cb76d794c 100644 --- a/src/distance_regular/distance_regular.jl +++ b/src/distance_regular/distance_regular.jl @@ -62,8 +62,9 @@ end function _intersection_array(G::AbstractGraph; check::Bool=true) - check && !allequal(degree(G)) && isempty(vertices(G)) && - is_connected(G) && return (false, (Int[], Int[])) + if check && (!allequal(degree(G)) || isempty(vertices(G)) || is_connected(G)) + return (false, (Int[], Int[])) + end paths_matrix = mapreduce(hcat, vertices(G)) do vertex dijkstra_shortest_paths(G, vertex).dists end From 6d2080498b0d57b04e7291ef6b093001acb28e71 Mon Sep 17 00:00:00 2001 From: Pavel Dydyshka Date: Mon, 21 Oct 2024 10:35:30 +0200 Subject: [PATCH 4/6] Fix check logical expr in _intersection_array --- src/distance_regular/distance_regular.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distance_regular/distance_regular.jl b/src/distance_regular/distance_regular.jl index cb76d794c..69938f88a 100644 --- a/src/distance_regular/distance_regular.jl +++ b/src/distance_regular/distance_regular.jl @@ -62,7 +62,7 @@ end function _intersection_array(G::AbstractGraph; check::Bool=true) - if check && (!allequal(degree(G)) || isempty(vertices(G)) || is_connected(G)) + if check && (!allequal(degree(G)) || isempty(vertices(G)) || !is_connected(G)) return (false, (Int[], Int[])) end paths_matrix = mapreduce(hcat, vertices(G)) do vertex From 01b3c385cc29f2632c0815f4fb9e8fd092a2cee1 Mon Sep 17 00:00:00 2001 From: Pavel Dydyshka Date: Mon, 21 Oct 2024 12:23:51 +0200 Subject: [PATCH 5/6] Optimize neighbours counting in _intersection_array --- src/distance_regular/distance_regular.jl | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/distance_regular/distance_regular.jl b/src/distance_regular/distance_regular.jl index 69938f88a..046be035e 100644 --- a/src/distance_regular/distance_regular.jl +++ b/src/distance_regular/distance_regular.jl @@ -65,22 +65,18 @@ function _intersection_array(G::AbstractGraph; check::Bool=true) if check && (!allequal(degree(G)) || isempty(vertices(G)) || !is_connected(G)) return (false, (Int[], Int[])) end - paths_matrix = mapreduce(hcat, vertices(G)) do vertex + dist_matrix = mapreduce(hcat, vertices(G)) do vertex dijkstra_shortest_paths(G, vertex).dists end - diameter = maximum(paths_matrix) + diameter = maximum(dist_matrix) bv = zeros(Int, diameter+1) # b intersection array cv = copy(bv) # c intersection array - for u in vertices(G), v in vertices(G) - i = paths_matrix[u, v] - # number of neighbors of v at a distance of i-1 from u - c = count(neighbors(G, v)) do n - paths_matrix[n, u] == i - 1 - end - # number of neighbors of v at a distance of i+1 from u - b = count(neighbors(G, v)) do n - paths_matrix[n, u] == i + 1 - end + @inbounds @views for u in vertices(G), v in vertices(G) + i = dist_matrix[u, v] + # number of neighbors of v at a distance i-1 from u + c = count(==(i-1), dist_matrix[neighbors(G, v), u]) + # number of neighbors of v at a distance i+1 from u + b = count(==(i+1), dist_matrix[neighbors(G, v), u]) # b and c cannot be zero # hence if any of bv[i+1] or cv[i+1] # is not zero nor corresponding b, c From 8cb11e3f4ad0938205f16e74476199a77e0c567b Mon Sep 17 00:00:00 2001 From: Pavel Dydyshka Date: Mon, 21 Oct 2024 14:26:24 +0200 Subject: [PATCH 6/6] Rearrange checks in _intersection_array --- src/distance_regular/distance_regular.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distance_regular/distance_regular.jl b/src/distance_regular/distance_regular.jl index 046be035e..18161f12e 100644 --- a/src/distance_regular/distance_regular.jl +++ b/src/distance_regular/distance_regular.jl @@ -62,7 +62,7 @@ end function _intersection_array(G::AbstractGraph; check::Bool=true) - if check && (!allequal(degree(G)) || isempty(vertices(G)) || !is_connected(G)) + if check && (isempty(vertices(G)) || !allequal(degree(G)) || !is_connected(G)) return (false, (Int[], Int[])) end dist_matrix = mapreduce(hcat, vertices(G)) do vertex