From a4038e207d4b1b3010f2a6fce80934a794e9845b Mon Sep 17 00:00:00 2001 From: Nenad Ilic Date: Thu, 12 Feb 2026 20:12:11 +0000 Subject: [PATCH 1/2] Add IGraphAlg dispatch for connectivity and biconnectivity algorithms New IGraphAlg methods that dispatch to the igraph C library: - connected_components: returns component membership as Vector{Vector{Int}} matching the Graphs.jl return format - is_connected: checks weak connectivity via igraph_is_connected - articulation: finds articulation points via igraph_articulation_points, returns sorted 1-based vertex IDs - bridges: finds bridges via igraph_bridges, converts edge IDs to SimpleEdge pairs All new methods accept any AbstractGraph and convert to IGraph internally, matching the pattern of the existing diameter/radius/has_isomorph dispatch. Tests verify output matches Graphs.jl for path, cycle, and disconnected graphs. All 281 tests pass. --- src/graph_api_extensions.jl | 41 ++++++++++++++++++++++-- test/test_graph_api_extensions.jl | 53 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/src/graph_api_extensions.jl b/src/graph_api_extensions.jl index ec21b95..2306aa7 100644 --- a/src/graph_api_extensions.jl +++ b/src/graph_api_extensions.jl @@ -1,11 +1,12 @@ # Explicit import/export of the functions # that are getting new methods, # so that `igraphalg_methods` can pick them up. -import Graphs: diameter, radius +import Graphs: diameter, radius, connected_components, is_connected, articulation, bridges import Graphs.Experimental import Graphs.Experimental: has_isomorph -export diameter, radius, has_isomorph +export diameter, radius, has_isomorph, + connected_components, is_connected, articulation, bridges struct IGraphAlg end @@ -31,3 +32,39 @@ end function has_isomorph(g1, g2, ::IGraphAlg) return LibIGraph.isomorphic(IGraph(g1), IGraph(g2))[1] end + +function connected_components(g, ::IGraphAlg) + ig = IGraph(g) + membership = IGVectorInt() + csize = IGVectorInt() + ncomp = LibIGraph.connected_components(ig, membership, csize, LibIGraph.IGRAPH_WEAK)[1] + # Convert igraph 0-based component IDs to Graphs.jl format: + # Vector of vectors, each containing 1-based vertex IDs + components = [Int[] for _ in 1:ncomp] + for (v, c) in enumerate(membership) + push!(components[c+1], v) + end + return components +end + +function is_connected(g, ::IGraphAlg) + return LibIGraph.is_connected(IGraph(g), LibIGraph.IGRAPH_WEAK)[1] +end + +function articulation(g, ::IGraphAlg) + ig = IGraph(g) + res = IGVectorInt() + LibIGraph.articulation_points(ig, res) + return sort!([v + 1 for v in res]) +end + +function bridges(g, ::IGraphAlg) + ig = IGraph(g) + res = IGVectorInt() + LibIGraph.bridges(ig, res) + # Convert edge IDs to SimpleEdge pairs + return [begin + (from, to) = LibIGraph.edge(ig, eid) + Graphs.SimpleGraphs.SimpleEdge(from+1, to+1) + end for eid in res] +end diff --git a/test/test_graph_api_extensions.jl b/test/test_graph_api_extensions.jl index ea5d600..6fd2ea3 100644 --- a/test/test_graph_api_extensions.jl +++ b/test/test_graph_api_extensions.jl @@ -20,4 +20,57 @@ using Random end end +@testset "connected_components" begin + # Connected graph + g = cycle_graph(5) + cc = connected_components(g, IGraphAlg()) + @test length(cc) == 1 + @test sort(cc[1]) == [1, 2, 3, 4, 5] + + # Disconnected graph: path(3) + isolated vertices + g2 = Graph(5) + add_edge!(g2, 1, 2) + add_edge!(g2, 2, 3) + add_edge!(g2, 4, 5) + cc2 = connected_components(g2, IGraphAlg()) + @test length(cc2) == 2 + cc2_sorted = sort(cc2; by=first) + @test sort(cc2_sorted[1]) == [1, 2, 3] + @test sort(cc2_sorted[2]) == [4, 5] + + # Match Graphs.jl result (same component grouping) + cc_ref = connected_components(g2) + @test length(cc2) == length(cc_ref) + @test Set(Set.(cc2)) == Set(Set.(cc_ref)) +end + +@testset "is_connected" begin + @test is_connected(cycle_graph(5), IGraphAlg()) == true + g = Graph(4) + add_edge!(g, 1, 2) + add_edge!(g, 3, 4) + @test is_connected(g, IGraphAlg()) == false + @test is_connected(g, IGraphAlg()) == is_connected(g) +end + +@testset "articulation" begin + g = path_graph(5) + art = articulation(g, IGraphAlg()) + art_ref = sort(articulation(g)) + @test art == art_ref + + g2 = cycle_graph(5) + @test isempty(articulation(g2, IGraphAlg())) +end + +@testset "bridges" begin + g = path_graph(5) + br = bridges(g, IGraphAlg()) + br_ref = bridges(g) + @test Set(br) == Set(br_ref) + + g2 = cycle_graph(5) + @test isempty(bridges(g2, IGraphAlg())) +end + end From e5bb5b391c1fb6a5270949222d9be8aa2e6fe4de Mon Sep 17 00:00:00 2001 From: Nenad Ilic Date: Thu, 12 Feb 2026 20:27:11 +0000 Subject: [PATCH 2/2] Update CHANGELOG.md with IGraphAlg dispatch additions --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9192490..6a6cde8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # News +## Unreleased + +- Add `IGraphAlg` dispatch for `connected_components`, `is_connected`, `articulation`, and `bridges`. + ## v1.0.0 - 2025-09-25 - Update the underlying igraph C library to v1.0.0.