Skip to content

Implement is_articulation to check if vertex is articulation point#387

Merged
Krastanov merged 10 commits intoJuliaGraphs:masterfrom
thchr:is-cut-vertex
Feb 27, 2026
Merged

Implement is_articulation to check if vertex is articulation point#387
Krastanov merged 10 commits intoJuliaGraphs:masterfrom
thchr:is-cut-vertex

Conversation

@thchr
Copy link
Contributor

@thchr thchr commented Jun 19, 2024

This factors out the DFS step from articulation into a separate function articulation_dfs! and then uses this new function to implement is_articulation(g, v) which checks whether v is an articulation point of g without necessarily computing all the articulation points.

The branching on isnothing(is_articulation_pts) in articulation_dfs! is a bit inelegant - as is the fact that articulation_dfs! doesn't mutate if is_articulation_pts === nothing, but it seemed the simplest way to avoid code duplication.

Aside from these additions, I revised the doc string of articulation(g) a bit (e.g., it 1. previously misleadingly stated that g had to be connected; it does not; and 2. it changed nomenclature from "articulation point" to "cut vertex" mid-sentence).

@thchr
Copy link
Contributor Author

thchr commented Jun 19, 2024

Timing-wise, is_articulation(g, v) appears to give a speed-up of about a factor of ~2 in cases I looked at, compared to v ∈ articulation(g):

julia> g = path_graph(5)
julia> articulation(g) # [2, 3, 4]
julia> @btime 2  articulation($g)   # 209.656 ns (8 allocations: 704 bytes)
julia> @btime is_articulation($g, 2) # 122.850 ns (4 allocations: 464 bytes)

julia> g = path_graph(15)
julia> articulation(g) # [2:14...]
julia> @btime 9  articulation($g)   # 453.853 ns (10 allocations: 2.16 KiB)
julia> @btime is_articulation($g, 9) # 245.286 ns (4 allocations: 624 bytes)

@codecov
Copy link

codecov bot commented Jun 19, 2024

Codecov Report

❌ Patch coverage is 98.21429% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.28%. Comparing base (e86eac3) to head (098ef79).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/biconnectivity/articulation.jl 98.21% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #387      +/-   ##
==========================================
- Coverage   97.29%   97.28%   -0.01%     
==========================================
  Files         126      126              
  Lines        7662     7674      +12     
==========================================
+ Hits         7455     7466      +11     
- Misses        207      208       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gdalle gdalle added the enhancement New feature or request label Jun 26, 2024
@thchr
Copy link
Contributor Author

thchr commented Aug 26, 2024

Bump.

@thchr
Copy link
Contributor Author

thchr commented Oct 31, 2024

Bump :)

Copy link
Member

@gdalle gdalle left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution, you were right to ping again

Project.toml Outdated
name = "Graphs"
uuid = "86223c79-3864-5bf0-83f7-82e725a168b6"
version = "1.11.1"
version = "1.11.2"
Copy link
Member

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

Comment on lines +70 to +72
s = Vector{Tuple{T,T,T}}()
low = zeros(T, nv(g))
pre = zeros(T, nv(g))
Copy link
Member

Choose a reason for hiding this comment

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

If the idea is to call is_articulation several times in a row for different vertices, these allocations might be reusable? Maybe we should expose articulation_dfs!?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, that'd be alright with me also. The _dfs part is an implementation detail though, so maybe this should just be called is_articulation!, taking work-arrays for pre and low then? Let me know what you think and I'll change to that.

Copy link
Member

Choose a reason for hiding this comment

The 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)

Copy link
Contributor Author

@thchr thchr Nov 21, 2024

Choose a reason for hiding this comment

The 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 work_buffer1 and work_buffer2 (adding some comments to explain in the code - or reassigning them to more aptly named variables) to avoid exposing unnecessary complications and implementation details to users.

Comment on lines +85 to +89
if !isnothing(is_articulation_pt)
if pre[u] != 0
return is_articulation_pt
end
end
Copy link
Member

Choose a reason for hiding this comment

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

Can you explain this shortcut in a comment?

thchr and others added 2 commits November 1, 2024 11:37
Co-authored-by: Guillaume Dalle <22795598+gdalle@users.noreply.github.com>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 25, 2026

Benchmark Results (Julia v1)

Time benchmarks
master 098ef79... master / 098ef79...
centrality/digraphs/betweenness_centrality 16 ± 0.77 ms 15.9 ± 0.92 ms 1 ± 0.076
centrality/digraphs/closeness_centrality 11 ± 0.43 ms 11 ± 0.42 ms 1 ± 0.055
centrality/digraphs/degree_centrality 2.07 ± 1.8 μs 1.9 ± 0.17 μs 1.09 ± 0.94
centrality/digraphs/katz_centrality 0.873 ± 0.1 ms 0.873 ± 0.09 ms 1 ± 0.16
centrality/digraphs/pagerank 0.0364 ± 0.0055 ms 0.0368 ± 0.00053 ms 0.991 ± 0.15
centrality/graphs/betweenness_centrality 28.7 ± 1.2 ms 29.1 ± 1 ms 0.987 ± 0.054
centrality/graphs/closeness_centrality 20.9 ± 0.091 ms 20.9 ± 0.12 ms 0.999 ± 0.0071
centrality/graphs/degree_centrality 1.54 ± 0.22 μs 1.49 ± 0.16 μs 1.03 ± 0.18
centrality/graphs/katz_centrality 1.03 ± 0.076 ms 1.03 ± 0.076 ms 1.01 ± 0.1
connectivity/digraphs/strongly_connected_components 0.0432 ± 0.0019 ms 0.0427 ± 0.0011 ms 1.01 ± 0.052
connectivity/graphs/connected_components 24.5 ± 0.92 μs 24.2 ± 0.62 μs 1.01 ± 0.046
core/edges/digraphs 7.02 ± 0.02 μs 7.02 ± 0.04 μs 1 ± 0.0064
core/edges/graphs 15.8 ± 0.091 μs 15.8 ± 0.081 μs 1 ± 0.0077
core/has_edge/digraphs 5.31 ± 0.59 μs 5.13 ± 0.37 μs 1.04 ± 0.14
core/has_edge/graphs 5.69 ± 0.8 μs 5.57 ± 0.39 μs 1.02 ± 0.16
core/nv/digraphs 0.361 ± 0.0033 μs 0.37 ± 0.01 μs 0.976 ± 0.028
core/nv/graphs 0.381 ± 0.011 μs 0.371 ± 0.011 μs 1.03 ± 0.042
edges/fille 8.2 ± 1.2 μs 8.58 ± 1.3 μs 0.956 ± 0.2
edges/fillp 4.65 ± 4.8 μs 4.81 ± 4.1 μs 0.967 ± 1.3
edges/tsume 2.58 ± 0.081 μs 2.65 ± 0.05 μs 0.977 ± 0.036
edges/tsump 2.69 ± 0.021 μs 2.62 ± 0.04 μs 1.02 ± 0.018
insertions/SG(n,e) Generation 25.2 ± 4.1 ms 24.8 ± 4.1 ms 1.01 ± 0.23
parallel/egonet/twohop 0.288 ± 0.0016 s 0.285 ± 0.00092 s 1.01 ± 0.0066
parallel/egonet/vertexfunction 2.27 ± 0.062 ms 2.2 ± 0.056 ms 1.03 ± 0.039
serial/egonet/twohop 0.29 ± 0.00064 s 0.283 ± 0.001 s 1.02 ± 0.0043
serial/egonet/vertexfunction 2.22 ± 0.048 ms 2.19 ± 0.057 ms 1.01 ± 0.034
traversals/digraphs/bfs_tree 0.049 ± 0.0096 ms 0.0495 ± 0.0089 ms 0.989 ± 0.26
traversals/digraphs/dfs_tree 0.0634 ± 0.0099 ms 0.0642 ± 0.0096 ms 0.988 ± 0.21
traversals/graphs/bfs_tree 0.0533 ± 0.0025 ms 0.0526 ± 0.0022 ms 1.01 ± 0.064
traversals/graphs/dfs_tree 0.0654 ± 0.0035 ms 0.0659 ± 0.0033 ms 0.992 ± 0.072
time_to_load 0.535 ± 0.0058 s 0.541 ± 0.004 s 0.989 ± 0.013
Memory benchmarks
master 098ef79... master / 098ef79...
centrality/digraphs/betweenness_centrality 0.29 M allocs: 24 MB 0.29 M allocs: 24 MB 1
centrality/digraphs/closeness_centrality 18.6 k allocs: 14.5 MB 18.6 k allocs: 14.5 MB 1
centrality/digraphs/degree_centrality 8 allocs: 5.01 kB 8 allocs: 5.01 kB 1
centrality/digraphs/katz_centrality 2.63 k allocs: 2.83 MB 2.63 k allocs: 2.83 MB 1
centrality/digraphs/pagerank 21 allocs: 14.9 kB 21 allocs: 14.9 kB 1
centrality/graphs/betweenness_centrality 0.545 M allocs: 0.0313 GB 0.545 M allocs: 0.0313 GB 1
centrality/graphs/closeness_centrality 19.3 k allocs: 14 MB 19.3 k allocs: 14 MB 1
centrality/graphs/degree_centrality 10 allocs: 5.43 kB 10 allocs: 5.43 kB 1
centrality/graphs/katz_centrality 2.96 k allocs: 3.1 MB 2.96 k allocs: 3.1 MB 1
connectivity/digraphs/strongly_connected_components 1.05 k allocs: 0.075 MB 1.05 k allocs: 0.075 MB 1
connectivity/graphs/connected_components 0.061 k allocs: 22.5 kB 0.061 k allocs: 22.5 kB 1
core/edges/digraphs 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
core/edges/graphs 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
core/has_edge/digraphs 20 allocs: 12.6 kB 20 allocs: 12.6 kB 1
core/has_edge/graphs 28 allocs: 13.8 kB 28 allocs: 13.8 kB 1
core/nv/digraphs 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
core/nv/graphs 3 allocs: 0.0938 kB 3 allocs: 0.0938 kB 1
edges/fille 3 allocs: 0.153 MB 3 allocs: 0.153 MB 1
edges/fillp 3 allocs: 0.153 MB 3 allocs: 0.153 MB 1
edges/tsume 0 allocs: 0 B 0 allocs: 0 B
edges/tsump 0 allocs: 0 B 0 allocs: 0 B
insertions/SG(n,e) Generation 0.0465 M allocs: 10.9 MB 0.0465 M allocs: 10.9 MB 0.999
parallel/egonet/twohop 10 allocs: 0.0768 MB 10 allocs: 0.0768 MB 1
parallel/egonet/vertexfunction 10 allocs: 0.0768 MB 10 allocs: 0.0768 MB 1
serial/egonet/twohop 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
serial/egonet/vertexfunction 3 allocs: 0.0764 MB 3 allocs: 0.0764 MB 1
traversals/digraphs/bfs_tree 2.34 k allocs: 0.113 MB 2.34 k allocs: 0.113 MB 1
traversals/digraphs/dfs_tree 2.44 k allocs: 0.118 MB 2.44 k allocs: 0.118 MB 1
traversals/graphs/bfs_tree 2.52 k allocs: 0.121 MB 2.52 k allocs: 0.121 MB 1
traversals/graphs/dfs_tree 2.63 k allocs: 0.127 MB 2.63 k allocs: 0.127 MB 1
time_to_load 0.149 k allocs: 11.1 kB 0.145 k allocs: 11 kB 1.02

@Krastanov
Copy link
Member

@thchr , do you have any outstanding things on this PR that you would like to do before having it merged?

I updated to current master and ran the formatter and added it to the changelog.

@thchr
Copy link
Contributor Author

thchr commented Feb 26, 2026

Thanks for picking this one up as well.

And no, there was no other things I wanted to do here. There was a request for a comment/explanation on some aspect of the code, but since this was effectively refactoring of an earlier implementation, I didn't know the explanation off-hand and didn't have time to look more.

For my sake, this is good to go as-is.

@thchr thchr requested a review from gdalle February 26, 2026 19:29
@Krastanov Krastanov merged commit ca5cbc3 into JuliaGraphs:master Feb 27, 2026
16 checks passed
@thchr thchr deleted the is-cut-vertex branch February 27, 2026 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants