-
Notifications
You must be signed in to change notification settings - Fork 96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement is_articulation
to check if vertex is articulation point
#387
base: master
Are you sure you want to change the base?
Changes from all commits
f7f3247
9a3c370
1e41e01
870597c
669093e
00f4843
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,9 @@ | ||
""" | ||
articulation(g) | ||
|
||
Compute the [articulation points](https://en.wikipedia.org/wiki/Biconnected_component) | ||
of a connected graph `g` and return an array containing all cut vertices. | ||
Compute the [articulation points](https://en.wikipedia.org/wiki/Biconnected_component) (also | ||
known as cut or seperating vertices) of an undirected graph `g` and return a vector | ||
containing all the vertices of `g` that are articulation points. | ||
|
||
# Examples | ||
```jldoctest | ||
|
@@ -22,74 +23,136 @@ julia> articulation(path_graph(5)) | |
function articulation end | ||
@traitfn function articulation(g::AG::(!IsDirected)) where {T,AG<:AbstractGraph{T}} | ||
s = Vector{Tuple{T,T,T}}() | ||
is_articulation_pt = falses(nv(g)) | ||
low = zeros(T, nv(g)) | ||
pre = zeros(T, nv(g)) | ||
|
||
is_articulation_pt = falses(nv(g)) | ||
@inbounds for u in vertices(g) | ||
pre[u] != 0 && continue | ||
v = u | ||
children = 0 | ||
wi::T = zero(T) | ||
w::T = zero(T) | ||
cnt::T = one(T) | ||
first_time = true | ||
|
||
# TODO the algorithm currently relies on the assumption that | ||
# outneighbors(g, v) is indexable. This assumption might not be true | ||
# in general, so in case that outneighbors does not produce a vector | ||
# we collect these vertices. This might lead to a large number of | ||
# allocations, so we should find a way to handle that case differently, | ||
# or require inneighbors, outneighbors and neighbors to always | ||
# return indexable collections. | ||
|
||
while !isempty(s) || first_time | ||
first_time = false | ||
if wi < 1 | ||
pre[v] = cnt | ||
cnt += 1 | ||
low[v] = pre[v] | ||
v_neighbors = collect_if_not_vector(outneighbors(g, v)) | ||
wi = 1 | ||
else | ||
wi, u, v = pop!(s) | ||
v_neighbors = collect_if_not_vector(outneighbors(g, v)) | ||
w = v_neighbors[wi] | ||
low[v] = min(low[v], low[w]) | ||
if low[w] >= pre[v] && u != v | ||
articulation_dfs!(is_articulation_pt, g, u, s, low, pre) | ||
end | ||
|
||
articulation_points = T[v for (v, b) in enumerate(is_articulation_pt) if b] | ||
|
||
return articulation_points | ||
end | ||
|
||
""" | ||
is_articulation(g, v) | ||
|
||
Determine whether `v` is an | ||
[articulation point](https://en.wikipedia.org/wiki/Biconnected_component) of an undirected | ||
graph `g`, returning `true` if so and `false` otherwise. | ||
|
||
See also [`articulation`](@ref). | ||
|
||
# Examples | ||
```jldoctest | ||
julia> using Graphs | ||
|
||
julia> g = path_graph(5) | ||
{5, 4} undirected simple Int64 graph | ||
|
||
julia> articulation(g) | ||
3-element Vector{Int64}: | ||
2 | ||
3 | ||
4 | ||
|
||
julia> is_articulation(g, 2) | ||
true | ||
|
||
julia> is_articulation(g, 1) | ||
false | ||
``` | ||
""" | ||
function is_articulation end | ||
@traitfn function is_articulation(g::AG::(!IsDirected), v::T) where {T,AG<:AbstractGraph{T}} | ||
s = Vector{Tuple{T,T,T}}() | ||
low = zeros(T, nv(g)) | ||
pre = zeros(T, nv(g)) | ||
Comment on lines
+70
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the idea is to call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, that'd be alright with me also. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good, but then we should at the very least explain how to initialize these arguments (and ideally what they mean) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See also discussion in #407 (comment): maybe we should just be calling these |
||
|
||
return articulation_dfs!(nothing, g, v, s, low, pre) | ||
end | ||
|
||
@traitfn function articulation_dfs!( | ||
is_articulation_pt::Union{Nothing,BitVector}, | ||
g::AG::(!IsDirected), | ||
u::T, | ||
s::Vector{Tuple{T,T,T}}, | ||
low::Vector{T}, | ||
pre::Vector{T}, | ||
gdalle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) where {T,AG<:AbstractGraph{T}} | ||
if !isnothing(is_articulation_pt) | ||
if pre[u] != 0 | ||
return is_articulation_pt | ||
end | ||
end | ||
Comment on lines
+85
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain this shortcut in a comment? |
||
|
||
v = u | ||
children = 0 | ||
wi::T = zero(T) | ||
w::T = zero(T) | ||
cnt::T = one(T) | ||
first_time = true | ||
|
||
# TODO the algorithm currently relies on the assumption that | ||
# outneighbors(g, v) is indexable. This assumption might not be true | ||
# in general, so in case that outneighbors does not produce a vector | ||
# we collect these vertices. This might lead to a large number of | ||
# allocations, so we should find a way to handle that case differently, | ||
# or require inneighbors, outneighbors and neighbors to always | ||
# return indexable collections. | ||
|
||
while !isempty(s) || first_time | ||
first_time = false | ||
if wi < 1 | ||
pre[v] = cnt | ||
cnt += 1 | ||
low[v] = pre[v] | ||
v_neighbors = collect_if_not_vector(outneighbors(g, v)) | ||
wi = 1 | ||
else | ||
wi, u, v = pop!(s) | ||
v_neighbors = collect_if_not_vector(outneighbors(g, v)) | ||
w = v_neighbors[wi] | ||
low[v] = min(low[v], low[w]) | ||
if low[w] >= pre[v] && u != v | ||
if isnothing(is_articulation_pt) | ||
if v == u | ||
return true | ||
end | ||
else | ||
is_articulation_pt[v] = true | ||
end | ||
wi += 1 | ||
end | ||
while wi <= length(v_neighbors) | ||
w = v_neighbors[wi] | ||
if pre[w] == 0 | ||
if u == v | ||
children += 1 | ||
end | ||
push!(s, (wi, u, v)) | ||
wi = 0 | ||
u = v | ||
v = w | ||
break | ||
elseif w != u | ||
low[v] = min(low[v], pre[w]) | ||
wi += 1 | ||
end | ||
while wi <= length(v_neighbors) | ||
w = v_neighbors[wi] | ||
if pre[w] == 0 | ||
if u == v | ||
children += 1 | ||
end | ||
wi += 1 | ||
push!(s, (wi, u, v)) | ||
wi = 0 | ||
u = v | ||
v = w | ||
break | ||
elseif w != u | ||
low[v] = min(low[v], pre[w]) | ||
end | ||
wi < 1 && continue | ||
wi += 1 | ||
end | ||
wi < 1 && continue | ||
end | ||
|
||
if children > 1 | ||
if children > 1 | ||
if isnothing(is_articulation_pt) | ||
return u == v | ||
else | ||
is_articulation_pt[u] = true | ||
end | ||
end | ||
|
||
articulation_points = Vector{T}() | ||
|
||
for u in findall(is_articulation_pt) | ||
push!(articulation_points, T(u)) | ||
end | ||
|
||
return articulation_points | ||
return isnothing(is_articulation_pt) ? false : is_articulation_pt | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
merge default branch into this PR, the version has evolved since