Skip to content
1 change: 1 addition & 0 deletions src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably mention here that

  • The digraph cannot have self-loops
  • indegree_sequence and outdegree_sequence are not independent, i.e. if entry i of indegree_sequence has value a_i and entry i of outdegree_sequence has value b_i then there will be a vertex with indegree a_i and outdegree b_i in the realization. (maybe there is an easier way to express this).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We modified the docstring trying to include your suggestions. Thanks.

### 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]))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also have a few edge cases here? Such as

  • Empty vectors
  • Graphs that only have a realization if there is a self-loop.
  • Graphs where the sum of the in and out degree sequences if the same, but there is still no valid realization.

Copy link
Member Author

@InterdisciplinaryPhysicsTeam InterdisciplinaryPhysicsTeam Dec 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've just noticed that isgraphical and isdigraphical don't work properly in the edge cases you mentioned.

For example:

  • isgraphical([2]) returns true even if it's not possible to realise a simple undirected graph (no self-loops, no multi-edges) with just a vertex with $d(v)=2$.
  • isdigraphical([1],[1]) returns true even if it's not possible to realise a simple directed graph (no self-loops, no multi-edges) with just a vertex with $d_{in}(v)=d_{out}(v)=1$.

# 1116
gc = cycle_graph(4)
for g in testgraphs(gc)
Expand Down