Skip to content

Commit

Permalink
Implement isdigraphical and fix isgraphical (#186)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Jan 14, 2023
1 parent 1a7594a commit c4360cf
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ export
neighborhood,
neighborhood_dists,
isgraphical,
isdigraphical,

# cycles
simplecycles_hawick_james,
Expand Down
81 changes: 78 additions & 3 deletions src/connectivity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -728,17 +728,28 @@ 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 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)
n = length(sorted_degs)
# Compute the length of the degree sequence
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]
Expand All @@ -748,3 +759,67 @@ 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 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 ``\\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=1}^n min(r-1,b_i) + \\sum_{i=r+1}^n min(r,b_i)
```
for each integer 1 <= 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
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
!(isempty(indegree_sequence) && isempty(outdegree_sequence)) || return true
# 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

_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
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
15 changes: 15 additions & 0 deletions test/connectivity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,21 @@
@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 simple digraphicality
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]))
# 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)
for g in testgraphs(gc)
Expand Down

0 comments on commit c4360cf

Please sign in to comment.