Skip to content

Commit c4360cf

Browse files
InterdisciplinaryPhysicsTeamClaudMorpitmonticone
authored
Implement isdigraphical and fix isgraphical (#186)
* Make `isgraphical` more robust and add `isdigraphical`` Co-Authored-By: Claudio Moroni <[email protected]> Co-Authored-By: Pietro Monticone <[email protected]>
1 parent 1a7594a commit c4360cf

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

src/Graphs.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ export
211211
neighborhood,
212212
neighborhood_dists,
213213
isgraphical,
214+
isdigraphical,
214215

215216
# cycles
216217
simplecycles_hawick_james,

src/connectivity.jl

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -728,17 +728,28 @@ According to Erdös-Gallai theorem, a degree sequence ``\\{d_1, ...,d_n\\}`` (so
728728
```math
729729
\\sum_{i=1}^{r} d_i \\leq r(r-1) + \\sum_{i=r+1}^n min(r,d_i)
730730
```
731-
for each integer r <= n-1
731+
for each integer r <= n-1.
732+
733+
See also: [`isdigraphical`](@ref)
732734
"""
733-
function isgraphical(degs::Vector{<:Integer})
735+
function isgraphical(degs::AbstractVector{<:Integer})
736+
# Check whether the degree sequence is empty
737+
!isempty(degs) || return true
738+
# Check whether the sum of degrees is even
734739
iseven(sum(degs)) || return false
740+
# Check that all degrees are non negative and less than n-1
741+
n = length(degs)
742+
all(0 .<= degs .<= n - 1) || return false
743+
# Sort the degree sequence in non-increasing order
735744
sorted_degs = sort(degs; rev=true)
736-
n = length(sorted_degs)
745+
# Compute the length of the degree sequence
737746
cur_sum = zero(UInt64)
747+
# Compute the minimum of each degree and the corresponding index
738748
mindeg = Vector{UInt64}(undef, n)
739749
@inbounds for i in 1:n
740750
mindeg[i] = min(i, sorted_degs[i])
741751
end
752+
# Check if the degree sequence satisfies the Erdös-Gallai condition
742753
cum_min = sum(mindeg)
743754
@inbounds for r in 1:(n - 1)
744755
cur_sum += sorted_degs[r]
@@ -748,3 +759,67 @@ function isgraphical(degs::Vector{<:Integer})
748759
end
749760
return true
750761
end
762+
763+
"""
764+
isdigraphical(indegree_sequence, outdegree_sequence)
765+
766+
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.
767+
768+
### Implementation Notes
769+
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 -
770+
```math
771+
\\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)
772+
```
773+
for each integer 1 <= r <= n-1.
774+
775+
See also: [`isgraphical`](@ref)
776+
"""
777+
function isdigraphical(
778+
indegree_sequence::AbstractVector{<:Integer},
779+
outdegree_sequence::AbstractVector{<:Integer},
780+
)
781+
# Check whether the degree sequences have the same length
782+
n = length(indegree_sequence)
783+
n == length(outdegree_sequence) || throw(
784+
ArgumentError("The indegree and outdegree sequences must have the same length.")
785+
)
786+
# Check whether the degree sequence is empty
787+
!(isempty(indegree_sequence) && isempty(outdegree_sequence)) || return true
788+
# Check all degrees are non negative and less than n-1
789+
all(0 .<= indegree_sequence .<= n - 1) || return false
790+
all(0 .<= outdegree_sequence .<= n - 1) || return false
791+
792+
sum(indegree_sequence) == sum(outdegree_sequence) || return false
793+
794+
_sortperm = sortperm(indegree_sequence; rev=true)
795+
796+
sorted_indegree_sequence = indegree_sequence[_sortperm]
797+
sorted_outdegree_sequence = outdegree_sequence[_sortperm]
798+
799+
indegree_sum = zero(Int64)
800+
outdegree_min_sum = zero(Int64)
801+
802+
cum_min = zero(Int64)
803+
804+
# The following approach, which requires substituting the line
805+
# cum_min = sum([min(sorted_outdegree_sequence[i], r) for i in (1+r):n])
806+
# with the line
807+
# cum_min -= mindeg[r]
808+
# 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
809+
#= mindeg = Vector{Int64}(undef, n)
810+
@inbounds for i = 1:n
811+
mindeg[i] = min(i, sorted_outdegree_sequence[i])
812+
end
813+
cum_min = sum(mindeg) =#
814+
# Similarly for `outdegree_min_sum`.
815+
816+
@inbounds for r in 1:n
817+
indegree_sum += sorted_indegree_sequence[r]
818+
outdegree_min_sum = sum([min(sorted_outdegree_sequence[i], r - 1) for i in 1:r])
819+
cum_min = sum([min(sorted_outdegree_sequence[i], r) for i in (1 + r):n])
820+
cond = indegree_sum <= (outdegree_min_sum + cum_min)
821+
cond || return false
822+
end
823+
824+
return true
825+
end

test/connectivity.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,21 @@
297297
@test @inferred(!isgraphical([1, 1, 1]))
298298
@test @inferred(isgraphical([2, 2, 2]))
299299
@test @inferred(isgraphical(fill(3, 10)))
300+
@test @inferred(isgraphical(Integer[]))
301+
##@test !@inferred(isgraphical([2]))
302+
303+
# Test simple digraphicality
304+
sdg = SimpleDiGraph(10, 90)
305+
@test @inferred(isdigraphical(indegree(sdg), outdegree(sdg)))
306+
@test !@inferred(isdigraphical([1, 1, 1], [1, 1, 0]))
307+
@test @inferred(isdigraphical(Integer[], Integer[]))
308+
#@test !@inferred(isdigraphical([1], [1]))
309+
# Self loops should be excluded
310+
@test !@inferred(isdigraphical([1], [1]))
311+
@test !@inferred(isdigraphical([1, 0], [1, 0]))
312+
# Multi edges should be excluded
313+
@test !@inferred(isdigraphical([5], [5]))
314+
300315
# 1116
301316
gc = cycle_graph(4)
302317
for g in testgraphs(gc)

0 commit comments

Comments
 (0)