Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
41 changes: 39 additions & 2 deletions src/graph_api_extensions.jl
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
53 changes: 53 additions & 0 deletions test/test_graph_api_extensions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading