From 21d9de240a27ed03edb2d5428ced71f7fcafb073 Mon Sep 17 00:00:00 2001 From: "Interdisciplinary Physics Team (InPhyT)" <63546437+InterdisciplinaryPhysicsTeam@users.noreply.github.com> Date: Fri, 16 Dec 2022 15:47:39 +0100 Subject: [PATCH 1/8] Make `isgraphical` more robust and add `isdigraphical`` Co-Authored-By: Claudio Moroni <43729990+ClaudMor@users.noreply.github.com> Co-Authored-By: Pietro Monticone <38562595+pitmonticone@users.noreply.github.com> --- src/connectivity.jl | 86 ++++++++++++++++++++++++++++++++++++++++++-- test/connectivity.jl | 10 ++++++ 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 174c3d72b..716bfd684 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -728,17 +728,29 @@ According to Erdös-Gallai theorem, a degree sequence ``\\{d_1, ...,d_n\\}`` (so ```math \\sum_{i=1}^{r} d_i \\leq r(r-1) + \\sum_{i=r+1}^n min(r,d_i) ``` -for each integer r <= n-1 +for each integer r <= n-1. + +See also: [`isdigraphical`](@ref) """ -function isgraphical(degs::Vector{<:Integer}) +function isgraphical(degs::AbstractVector{<:Integer}) + # Check whether the degree sequence is empty + !isempty(degs) || return true + # Check whether the degree sequence has only non-negative values + all(degs .>= 0) || + throw(ArgumentError("The degree sequence must contain non-negative integers only.")) + # Check whether the sum of degrees is even iseven(sum(degs)) || return false + # Sort the degree sequence in non-increasing order sorted_degs = sort(degs; rev=true) + # Compute the length of the degree sequence n = length(sorted_degs) cur_sum = zero(UInt64) + # Compute the minimum of each degree and the corresponding index mindeg = Vector{UInt64}(undef, n) @inbounds for i in 1:n mindeg[i] = min(i, sorted_degs[i]) end + # Check if the degree sequence satisfies the Erdös-Gallai condition cum_min = sum(mindeg) @inbounds for r in 1:(n - 1) cur_sum += sorted_degs[r] @@ -748,3 +760,73 @@ function isgraphical(degs::Vector{<:Integer}) end return true end + +""" + isdigraphical(indegree_sequence, outdegree_sequence) + +Check whether the given indegree sequence and outdegree sequence are digraphical, that is whether they can be the indegree and outdegree sequence of a digraph. + +### Implementation Notes +According to Fulkerson-Chen-Anstee theorem, a sequence ``\\{(a_1, b_1), ...,(a_n, b_n)\\}`` (sorted in descending order of a) is graphic iff the sum of vertex degrees is even and ``\\sum_{i = 1}^{n} a_i = \\sum_{i = 1}^{n} b_i\\}`` and the sequence obeys the property - +```math +\\sum_{i=1}^{r} a_i \\leq \\sum_{i=r+1}^n min(r-1,b_i) + \\sum_{i=r+1}^n min(r,b_i) +``` +for each integer r <= n-1. + +See also: [`isgraphical`](@ref) +""" +function isdigraphical( + indegree_sequence::AbstractVector{<:Integer}, + outdegree_sequence::AbstractVector{<:Integer}, +) + # Check whether the degree sequences have the same length + length(indegree_sequence) == length(outdegree_sequence) || throw( + ArgumentError("The indegree and outdegree sequences must have the same length.") + ) + # Check whether the degree sequence is empty + !(isempty(indegree_sequence) && isempty(outdegree_sequence)) || return true + # Check whether the degree sequences have only non-negative values + all(indegree_sequence .>= 0) || throw( + ArgumentError("The indegree sequence must contain non-negative integers only.") + ) + all(outdegree_sequence .>= 0) || throw( + ArgumentError("The outdegree sequence must contain non-negative integers only.") + ) + + n = length(indegree_sequence) + n == length(outdegree_sequence) || return false + + sum(indegree_sequence) == sum(outdegree_sequence) || return false + + _sortperm = sortperm(indegree_sequence; rev=true) + + sorted_indegree_sequence = indegree_sequence[_sortperm] + sorted_outdegree_sequence = outdegree_sequence[_sortperm] + + indegree_sum = zero(Int64) + outdegree_min_sum = zero(Int64) + + cum_min = zero(Int64) + + # The following approach, which requires substituting the line + # cum_min = sum([min(sorted_outdegree_sequence[i], r) for i in (1+r):n]) + # with the line + # cum_min -= mindeg[r] + # inside the for loop below, work as well, but the values of `cum_min` at each iteration differ. To be on the safe side we implemented it as in https://en.wikipedia.org/wiki/Fulkerson%E2%80%93Chen%E2%80%93Anstee_theorem + #= mindeg = Vector{Int64}(undef, n) + @inbounds for i = 1:n + mindeg[i] = min(i, sorted_outdegree_sequence[i]) + end + cum_min = sum(mindeg) =# + # Similarly for `outdegree_min_sum`. + + @inbounds for r in 1:(n - 1) + indegree_sum += sorted_indegree_sequence[r] + outdegree_min_sum = sum([min(sorted_outdegree_sequence[i], r - 1) for i in 1:r]) + cum_min = sum([min(sorted_outdegree_sequence[i], r) for i in (1 + r):n]) + cond = indegree_sum <= (outdegree_min_sum + cum_min) + cond || return false + end + + return true +end \ No newline at end of file diff --git a/test/connectivity.jl b/test/connectivity.jl index ae6adebf3..748adee9e 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -297,6 +297,16 @@ @test @inferred(!isgraphical([1, 1, 1])) @test @inferred(isgraphical([2, 2, 2])) @test @inferred(isgraphical(fill(3, 10))) + @test @inferred(isgraphical(Integer[])) + ##@test !@inferred(isgraphical([2])) + + # Test directed simple graphicality + sdg = SimpleDiGraph(10, 90) + @test @inferred(isdigraphical(indegree(sdg), outdegree(sdg))) + @test !@inferred(isdigraphical([1, 1, 1], [1, 1, 0])) + @test @inferred(isdigraphical(Integer[], Integer[])) + #@test !@inferred(isdigraphical([1], [1])) + # 1116 gc = cycle_graph(4) for g in testgraphs(gc) From e430a25da89a6ffb98fe3c3925c47b615fce1481 Mon Sep 17 00:00:00 2001 From: "Interdisciplinary Physics Team (InPhyT)" <63546437+InterdisciplinaryPhysicsTeam@users.noreply.github.com> Date: Fri, 16 Dec 2022 15:48:43 +0100 Subject: [PATCH 2/8] Update connectivity.jl Co-Authored-By: Claudio Moroni <43729990+ClaudMor@users.noreply.github.com> Co-Authored-By: Pietro Monticone <38562595+pitmonticone@users.noreply.github.com> --- test/connectivity.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/connectivity.jl b/test/connectivity.jl index 748adee9e..75ea3ddd2 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -306,7 +306,7 @@ @test !@inferred(isdigraphical([1, 1, 1], [1, 1, 0])) @test @inferred(isdigraphical(Integer[], Integer[])) #@test !@inferred(isdigraphical([1], [1])) - + # 1116 gc = cycle_graph(4) for g in testgraphs(gc) @@ -321,4 +321,4 @@ @test (4, 1) ∈ z @test (4, 2) ∉ z end -end +end \ No newline at end of file From 94318833581ad0f062786a68adc4bc4d5b08db08 Mon Sep 17 00:00:00 2001 From: "Interdisciplinary Physics Team (InPhyT)" <63546437+InterdisciplinaryPhysicsTeam@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:03:01 +0100 Subject: [PATCH 3/8] Format src/connectivity.jl and test/connectivity.jl Co-Authored-By: Claudio Moroni <43729990+ClaudMor@users.noreply.github.com> Co-Authored-By: Pietro Monticone <38562595+pitmonticone@users.noreply.github.com> --- src/connectivity.jl | 2 +- test/connectivity.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 716bfd684..f4d1f5325 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -829,4 +829,4 @@ function isdigraphical( end return true -end \ No newline at end of file +end diff --git a/test/connectivity.jl b/test/connectivity.jl index 75ea3ddd2..b171322b2 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -299,7 +299,7 @@ @test @inferred(isgraphical(fill(3, 10))) @test @inferred(isgraphical(Integer[])) ##@test !@inferred(isgraphical([2])) - + # Test directed simple graphicality sdg = SimpleDiGraph(10, 90) @test @inferred(isdigraphical(indegree(sdg), outdegree(sdg))) @@ -321,4 +321,4 @@ @test (4, 1) ∈ z @test (4, 2) ∉ z end -end \ No newline at end of file +end From a250beda93e5d8b148420a4832ac8f619a92d10b Mon Sep 17 00:00:00 2001 From: "Interdisciplinary Physics Team (InPhyT)" <63546437+InterdisciplinaryPhysicsTeam@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:49:45 +0100 Subject: [PATCH 4/8] Export `isdigraphical` Co-Authored-By: Claudio Moroni <43729990+ClaudMor@users.noreply.github.com> Co-Authored-By: Pietro Monticone <38562595+pitmonticone@users.noreply.github.com> --- src/Graphs.jl | 1 + test/connectivity.jl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Graphs.jl b/src/Graphs.jl index 171204112..db764fd01 100644 --- a/src/Graphs.jl +++ b/src/Graphs.jl @@ -209,6 +209,7 @@ export neighborhood, neighborhood_dists, isgraphical, + isdigraphical, # cycles simplecycles_hawick_james, diff --git a/test/connectivity.jl b/test/connectivity.jl index b171322b2..2040b1035 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -300,7 +300,7 @@ @test @inferred(isgraphical(Integer[])) ##@test !@inferred(isgraphical([2])) - # Test directed simple graphicality + # Test simple digraphicality sdg = SimpleDiGraph(10, 90) @test @inferred(isdigraphical(indegree(sdg), outdegree(sdg))) @test !@inferred(isdigraphical([1, 1, 1], [1, 1, 0])) From e8d62580b6068e50ba29a45a5b142bd1289bb4b2 Mon Sep 17 00:00:00 2001 From: "Interdisciplinary Physics Team (InPhyT)" <63546437+InterdisciplinaryPhysicsTeam@users.noreply.github.com> Date: Fri, 13 Jan 2023 16:31:47 +0100 Subject: [PATCH 5/8] Fix `isdigraphical`'s main loop, docstring and checks Co-Authored-By: Pietro Monticone <38562595+pitmonticone@users.noreply.github.com> Co-Authored-By: Claudio Moroni <43729990+ClaudMor@users.noreply.github.com> --- src/connectivity.jl | 12 +++++------- test/connectivity.jl | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index f4d1f5325..198012005 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -764,10 +764,10 @@ end """ isdigraphical(indegree_sequence, outdegree_sequence) -Check whether the given indegree sequence and outdegree sequence are digraphical, that is whether they can be the indegree and outdegree sequence of a digraph. +Check whether the given indegree sequence and outdegree sequence are digraphical, that is whether they can be the indegree and outdegree sequence of a simple digraph (i.e. a directed graph with no loops). This implies that `indegree_sequence` and `outdegree_sequence` are not independent, as their elements respectively represent the indegrees and outdegrees that the vertices shall have. ### Implementation Notes -According to Fulkerson-Chen-Anstee theorem, a sequence ``\\{(a_1, b_1), ...,(a_n, b_n)\\}`` (sorted in descending order of a) is graphic iff the sum of vertex degrees is even and ``\\sum_{i = 1}^{n} a_i = \\sum_{i = 1}^{n} b_i\\}`` and the sequence obeys the property - +According to Fulkerson-Chen-Anstee theorem, a sequence ``\\{(a_1, b_1), ...,(a_n, b_n)\\}`` (sorted in descending order of a) is graphic iff ``\\sum_{i = 1}^{n} a_i = \\sum_{i = 1}^{n} b_i\\}`` and the sequence obeys the property - ```math \\sum_{i=1}^{r} a_i \\leq \\sum_{i=r+1}^n min(r-1,b_i) + \\sum_{i=r+1}^n min(r,b_i) ``` @@ -780,7 +780,8 @@ function isdigraphical( outdegree_sequence::AbstractVector{<:Integer}, ) # Check whether the degree sequences have the same length - length(indegree_sequence) == length(outdegree_sequence) || throw( + n = length(indegree_sequence) + n == length(outdegree_sequence) || throw( ArgumentError("The indegree and outdegree sequences must have the same length.") ) # Check whether the degree sequence is empty @@ -793,9 +794,6 @@ function isdigraphical( ArgumentError("The outdegree sequence must contain non-negative integers only.") ) - n = length(indegree_sequence) - n == length(outdegree_sequence) || return false - sum(indegree_sequence) == sum(outdegree_sequence) || return false _sortperm = sortperm(indegree_sequence; rev=true) @@ -820,7 +818,7 @@ function isdigraphical( cum_min = sum(mindeg) =# # Similarly for `outdegree_min_sum`. - @inbounds for r in 1:(n - 1) + @inbounds for r in 1:n indegree_sum += sorted_indegree_sequence[r] outdegree_min_sum = sum([min(sorted_outdegree_sequence[i], r - 1) for i in 1:r]) cum_min = sum([min(sorted_outdegree_sequence[i], r) for i in (1 + r):n]) diff --git a/test/connectivity.jl b/test/connectivity.jl index 2040b1035..4793eac67 100644 --- a/test/connectivity.jl +++ b/test/connectivity.jl @@ -306,6 +306,11 @@ @test !@inferred(isdigraphical([1, 1, 1], [1, 1, 0])) @test @inferred(isdigraphical(Integer[], Integer[])) #@test !@inferred(isdigraphical([1], [1])) + # Self loops should be excluded + @test !@inferred(isdigraphical([1], [1])) + @test !@inferred(isdigraphical([1, 0], [1, 0])) + # Multi edges should be excluded + @test !@inferred(isdigraphical([5], [5])) # 1116 gc = cycle_graph(4) From 3c82b520512d66fa33cb0d1ac3cc4e35342091a8 Mon Sep 17 00:00:00 2001 From: "Interdisciplinary Physics Team (InPhyT)" Date: Sat, 14 Jan 2023 10:58:37 +0100 Subject: [PATCH 6/8] Update src/connectivity.jl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Simon Schölly --- src/connectivity.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 198012005..169796671 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -769,7 +769,7 @@ Check whether the given indegree sequence and outdegree sequence are digraphical ### Implementation Notes According to Fulkerson-Chen-Anstee theorem, a sequence ``\\{(a_1, b_1), ...,(a_n, b_n)\\}`` (sorted in descending order of a) is graphic iff ``\\sum_{i = 1}^{n} a_i = \\sum_{i = 1}^{n} b_i\\}`` and the sequence obeys the property - ```math -\\sum_{i=1}^{r} a_i \\leq \\sum_{i=r+1}^n min(r-1,b_i) + \\sum_{i=r+1}^n min(r,b_i) +\\sum_{i=1}^{r} a_i \\leq \\sum_{i=1}^n min(r-1,b_i) + \\sum_{i=r+1}^n min(r,b_i) ``` for each integer r <= n-1. From 76b1bd587e706ee7be8c4da17c13f3181f94537b Mon Sep 17 00:00:00 2001 From: "Interdisciplinary Physics Team (InPhyT)" Date: Sat, 14 Jan 2023 10:59:01 +0100 Subject: [PATCH 7/8] Update src/connectivity.jl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Simon Schölly --- src/connectivity.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 169796671..3ff996144 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -771,7 +771,7 @@ According to Fulkerson-Chen-Anstee theorem, a sequence ``\\{(a_1, b_1), ...,(a_n ```math \\sum_{i=1}^{r} a_i \\leq \\sum_{i=1}^n min(r-1,b_i) + \\sum_{i=r+1}^n min(r,b_i) ``` -for each integer r <= n-1. +for each integer 1 <= r <= n-1. See also: [`isgraphical`](@ref) """ From f2694a65e8c755f5166ab535d266eaf399491e7e Mon Sep 17 00:00:00 2001 From: "Interdisciplinary Physics Team (InPhyT)" <63546437+InterdisciplinaryPhysicsTeam@users.noreply.github.com> Date: Sat, 14 Jan 2023 11:00:51 +0100 Subject: [PATCH 8/8] Update connectivity.jl Co-Authored-By: Pietro Monticone <38562595+pitmonticone@users.noreply.github.com> Co-Authored-By: Claudio Moroni <43729990+ClaudMor@users.noreply.github.com> --- src/connectivity.jl | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/connectivity.jl b/src/connectivity.jl index 3ff996144..fde1dad74 100644 --- a/src/connectivity.jl +++ b/src/connectivity.jl @@ -735,15 +735,14 @@ See also: [`isdigraphical`](@ref) function isgraphical(degs::AbstractVector{<:Integer}) # Check whether the degree sequence is empty !isempty(degs) || return true - # Check whether the degree sequence has only non-negative values - all(degs .>= 0) || - throw(ArgumentError("The degree sequence must contain non-negative integers only.")) # Check whether the sum of degrees is even iseven(sum(degs)) || return false + # Check that all degrees are non negative and less than n-1 + n = length(degs) + all(0 .<= degs .<= n - 1) || return false # Sort the degree sequence in non-increasing order sorted_degs = sort(degs; rev=true) # Compute the length of the degree sequence - n = length(sorted_degs) cur_sum = zero(UInt64) # Compute the minimum of each degree and the corresponding index mindeg = Vector{UInt64}(undef, n) @@ -786,13 +785,9 @@ function isdigraphical( ) # Check whether the degree sequence is empty !(isempty(indegree_sequence) && isempty(outdegree_sequence)) || return true - # Check whether the degree sequences have only non-negative values - all(indegree_sequence .>= 0) || throw( - ArgumentError("The indegree sequence must contain non-negative integers only.") - ) - all(outdegree_sequence .>= 0) || throw( - ArgumentError("The outdegree sequence must contain non-negative integers only.") - ) + # Check all degrees are non negative and less than n-1 + all(0 .<= indegree_sequence .<= n - 1) || return false + all(0 .<= outdegree_sequence .<= n - 1) || return false sum(indegree_sequence) == sum(outdegree_sequence) || return false