Skip to content

Commit

Permalink
Add support for edge weights from sparse matrix input.
Browse files Browse the repository at this point in the history
This patch adds the weights keyword argument to Metis.graph to construct
a Metis.Graph with edge weights based on the matrix entries, i.e.:

    g = Metis.graph(A; weights=true)

Note that the weights must be integers.
  • Loading branch information
fredrikekre committed Jul 12, 2022
1 parent 0a3c662 commit e42fded
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 8 deletions.
18 changes: 14 additions & 4 deletions src/Metis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,30 @@ struct Graph
xadj::Vector{idx_t}
adjncy::Union{Vector{idx_t}, Ptr{Nothing}}
vwgt::Union{Vector{idx_t}, Ptr{Nothing}}
Graph(nvtxs, xadj, adjncy, vwgt=C_NULL) = new(nvtxs, xadj, adjncy, vwgt)
adjwgt::Union{Vector{idx_t}, Ptr{Nothing}}
function Graph(nvtxs, xadj, adjncy, vwgt=C_NULL, adjwgt=C_NULL)
return new(nvtxs, xadj, adjncy, vwgt, adjwgt)
end
end

"""
Metis.graph(G::SparseMatrixCSC; check_hermitian=true)
Metis.graph(G::SparseMatrixCSC; weights=false, check_hermitian=true)
Construct the 1-based CSR representation of the sparse matrix `G`.
If `check_hermitian` is `false` the matrix is not checked for being hermitian
before constructing the graph.
If `weights=true` the entries of the matrix are used as edge weights.
"""
function graph(G::SparseMatrixCSC; check_hermitian=true)
function graph(G::SparseMatrixCSC; weights::Bool=false, check_hermitian::Bool=true)
if check_hermitian
ishermitian(G) || throw(ArgumentError("matrix must be Hermitian"))
end
N = size(G, 1)
xadj = Vector{idx_t}(undef, N+1)
xadj[1] = 1
adjncy = Vector{idx_t}(undef, nnz(G))
vwgt = C_NULL # TODO: Vertex weights could be passed as input argument
adjwgt = weights ? Vector{idx_t}(undef, nnz(G)) : C_NULL
adjncy_i = 0
@inbounds for j in 1:N
n_rows = 0
Expand All @@ -49,12 +55,16 @@ function graph(G::SparseMatrixCSC; check_hermitian=true)
n_rows += 1
adjncy_i += 1
adjncy[adjncy_i] = i
if weights
adjwgt[adjncy_i] = G.nzval[k]
end
end
end
xadj[j+1] = xadj[j] + n_rows
end
resize!(adjncy, adjncy_i)
return Graph(idx_t(N), xadj, adjncy)
weights && resize!(adjwgt, adjncy_i)
return Graph(idx_t(N), xadj, adjncy, vwgt, adjwgt)
end

"""
Expand Down
26 changes: 22 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,25 @@ using Test
using SparseArrays
import LightGraphs, Graphs

@testset "Metis.graph(::SparseMatrixCSC)" begin
rng = MersenneTwister(0)
S = sprand(rng, Int, 10, 10, 0.2); S = S + S'; fill!(S.nzval, 1)
foreach(i -> S[i, i] = 0, 1:10); dropzeros!(S)
g = Metis.graph(S)
gw = Metis.graph(S; weights=true)
@test g.xadj == gw.xadj
@test g.adjncy == gw.adjncy
@test g.adjwgt == C_NULL
@test gw.adjwgt == ones(Int, length(gw.adjncy))
G = SparseMatrixCSC(10, 10, g.xadj, g.adjncy, ones(Int, length(g.adjncy)))
GW = SparseMatrixCSC(10, 10, gw.xadj, gw.adjncy, gw.adjwgt)
@test iszero(S - G)
@test iszero(S - GW)
end

@testset "Metis.permutation" begin
rng = MersenneTwister(0)
S = sprand(rng, 10, 10, 0.5); S = S + S'; fill(S.nzval, 1)
S = sprand(rng, 10, 10, 0.5); S = S + S'; fill!(S.nzval, 1)
perm, iperm = Metis.permutation(S)
@test isperm(perm)
@test isperm(iperm)
Expand All @@ -15,10 +31,11 @@ end

@testset "Metis.partition" begin
rng = MersenneTwister(0)
S = sprand(rng, 10, 10, 0.5); S = S + S'; fill(S.nzval, 1)
S = sprand(rng, Int, 10, 10, 0.5); S = S + S'; fill!(S.nzval, 1)
SG = Metis.graph(S; weights=true)
T = Graphs.smallgraph(:tutte)
TL = LightGraphs.smallgraph(:tutte)
for G in (S, T, TL), alg in (:RECURSIVE, :KWAY), nparts in (3, 4)
for G in (S, T, TL, SG), alg in (:RECURSIVE, :KWAY), nparts in (3, 4)
partition = Metis.partition(G, nparts, alg = alg)
@test extrema(partition) == (1, nparts)
@test all(x -> findfirst(==(x), partition) !== nothing, 1:nparts)
Expand All @@ -27,7 +44,8 @@ end

@testset "Metis.separator" begin
rng = MersenneTwister(0)
S = sprand(rng, 10, 10, 0.5); S = S + S'; fill(S.nzval, 1)
S = sprand(rng, 10, 10, 0.5); S = S + S'; fill!(S.nzval, 1)
SG = Metis.graph(S; weights=true)
T = Graphs.smallgraph(:tutte)
TL = LightGraphs.smallgraph(:tutte)
for G in (S, T, TL)
Expand Down

0 comments on commit e42fded

Please sign in to comment.