diff --git a/Project.toml b/Project.toml index 0ad8e2b..41c5122 100644 --- a/Project.toml +++ b/Project.toml @@ -19,7 +19,8 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "Documenter", "Graphs", "JuliaFormatter", "LinearAlgebra", "Test"] +test = ["Aqua", "Documenter", "Graphs", "JuliaFormatter", "LinearAlgebra", "SparseArrays", "Test"] diff --git a/src/overrides.jl b/src/overrides.jl index f943672..389491e 100644 --- a/src/overrides.jl +++ b/src/overrides.jl @@ -162,12 +162,50 @@ Compute the weighted subgraph induced by a list of vertices. Return a tuple containing the new graph and the list of vertices. """ function Graphs.induced_subgraph( - g::T, vlist::AbstractVector{U} -) where {T<:AbstractSimpleWeightedGraph,U<:Integer} - E = eltype(g) + g::G, vlist::AbstractVector{U} +) where {G<:AbstractSimpleWeightedGraph,U<:Integer} + T = eltype(g) allunique(vlist) || throw(ArgumentError("Vertices in subgraph list must be unique")) - new_weights = g.weights[E.(vlist), E.(vlist)] + new_weights = g.weights[T.(vlist), T.(vlist)] newg = zero(g) newg.weights = new_weights - return newg, Vector{E}(vlist) + return newg, Vector{T}(vlist) +end + +function Graphs.induced_subgraph( + g::G, elist::AbstractVector{E} +) where {G<:AbstractSimpleWeightedGraph} where {E<:AbstractEdge} + allunique(elist) || throw(ArgumentError("Edges in subgraph list must be unique")) + T, U = eltype(g), weighttype(g) + vertex_set = Set{T}() + for e in elist + if has_edge(g, e) + push!(vertex_set, src(e), dst(e)) + else + @warn "Skipping the edge $(e), since it does not exist in the graph!" + end + end + vertex_list = collect(vertex_set) + sort!(vertex_list) + index_map = Dict(vertex_list[i] => i for i in eachindex(vertex_list)) + n = length(vertex_list) + new_weights = spzeros(weighttype(g), T, n, n) + I, J, W = T[], T[], U[] + for e in elist + if has_edge(g, e) + i, j = index_map[src(e)], index_map[dst(e)] + w = get_weight(g, dst(e), src(e)) + push!(I, j) # storage is transposed! + push!(J, i) + push!(W, w) + if !is_directed(g) + push!(I, i) + push!(J, j) + push!(W, w) + end + end + end + new_weights = sparse(I, J, W) + newg = G(new_weights) + return newg, vertex_list end diff --git a/test/simpleweightedgraph.jl b/test/simpleweightedgraph.jl index b6ecad2..adb7ce7 100644 --- a/test/simpleweightedgraph.jl +++ b/test/simpleweightedgraph.jl @@ -1,4 +1,5 @@ using SimpleWeightedGraphs +using SparseArrays @testset verbose = true "SimpleWeightedGraphs" begin @info("Ignore warnings relating to adding and removing vertices and edges") @@ -351,4 +352,33 @@ using SimpleWeightedGraphs @test g[1, 3, Val{:weight}()] ≈ 0 @test g[2, 3, Val{:weight}()] ≈ 0.5 end + + # this testset was implemented for https://github.com/JuliaGraphs/SimpleWeightedGraphs.jl/issues/32 + @testset "induced_subgraph should preserve weights for edge lists" begin + g = SimpleWeightedGraph([0 2; 2 0]) + expected_graph_weights = sparse([0 2; 2 0]) + # vertex induced subgraph + vertex_induced_subgraph_weights = weights(first(induced_subgraph(g, [1, 2]))) + @test vertex_induced_subgraph_weights == expected_graph_weights + # edge induced subgraph + edge_induced_subgraph_weights = weights(first(induced_subgraph(g, [Edge(1, 2)]))) + @test edge_induced_subgraph_weights == expected_graph_weights + + # test edge induced graph with one edge removed + # graph isomorphic to C_5 + g = SimpleWeightedGraph([0 2 0 0 2; 2 0 2 0 0; 0 2 0 2 0; 0 0 2 0 2; 2 0 0 2 0]) + expected_graph_weights = sparse( + [0 2 0 0 0; 2 0 2 0 0; 0 2 0 2 0; 0 0 2 0 2; 0 0 0 2 0] + ) + # create edge induced subgraph isomorphic to P_5. The edge (1, 5) is missing and test if weights are correct. + edge_induced_subgraph_weights = weights( + first(induced_subgraph(g, [Edge(1, 2), Edge(2, 3), Edge(3, 4), Edge(4, 5)])) + ) + @test edge_induced_subgraph_weights == expected_graph_weights + + # test edge induced subgraph which does not contain the whole vertex set, especially remove the first column (vertex 1) + edge_induced_subgraph_weights = weights(first(induced_subgraph(g, [Edge(2, 3)]))) + expected_graph_weights = sparse([0 2; 2 0]) + @test edge_induced_subgraph_weights == expected_graph_weights + end end