From 5415516c6fb9336469ab952982183e8f7085143c Mon Sep 17 00:00:00 2001 From: SANDO Keishi <50182133+KeishiS@users.noreply.github.com> Date: Sat, 4 May 2024 17:37:06 +0900 Subject: [PATCH] Modify `non_backtracking_matrix` function's Return Type (#328) * modified to use a sparse matrix in non_backtracking_matrix * modified to reserve a capacity of size 'nz' & used 'append!' instead of 'push!' * added tests for non_backtracking_matrix * modified to achieve zero allocation * reverted * Apply suggestions from code review --------- Co-authored-by: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> --- src/linalg/nonbacktracking.jl | 14 ++++++-- test/linalg/nonbacktracking.jl | 59 ++++++++++++++++++++++++++++++++++ test/linalg/runtests.jl | 2 +- 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 test/linalg/nonbacktracking.jl diff --git a/src/linalg/nonbacktracking.jl b/src/linalg/nonbacktracking.jl index ec88a59f7..ddbae17dd 100644 --- a/src/linalg/nonbacktracking.jl +++ b/src/linalg/nonbacktracking.jl @@ -26,17 +26,25 @@ function non_backtracking_matrix(g::AbstractGraph) end end - B = zeros(Float64, m, m) - + nz = if is_directed(g) + sum(indegree(g, v) * outdegree(g, v) for v in vertices(g)) + else + sum(degree(g, v) * (degree(g, v) - 1) for v in vertices(g)) + end + rowidx = sizehint!(Vector{Int}(), nz) + colidx = sizehint!(Vector{Int}(), nz) for (e, u) in edgeidmap i, j = src(e), dst(e) for k in inneighbors(g, i) k == j && continue v = edgeidmap[Edge(k, i)] - B[v, u] = 1 + + push!(rowidx, v) + push!(colidx, u) end end + B = sparse(rowidx, colidx, ones(Int, length(rowidx)), m, m) return B, edgeidmap end diff --git a/test/linalg/nonbacktracking.jl b/test/linalg/nonbacktracking.jl new file mode 100644 index 000000000..82589038f --- /dev/null +++ b/test/linalg/nonbacktracking.jl @@ -0,0 +1,59 @@ +using SparseArrays + +@testset "nonbacktracking" begin + # Case: simple undirected + ug = path_graph(5) + B, edgemap = non_backtracking_matrix(ug) + # | 1->2 | 2->3 | 3->4 | 4->5 | 2->1 | 3->2 | 4->3 | 5->4 + # ------------------------------------------------------------- + # 1->2 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 + # 2->3 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 + # 3->4 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 + # 4->5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 + # 2->1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 + # 3->2 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 + # 4->3 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 + # 5->4 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 + B_ = [ + 0 1 0 0 0 0 0 0 + 0 0 1 0 0 0 0 0 + 0 0 0 1 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 1 0 0 0 + 0 0 0 0 0 1 0 0 + 0 0 0 0 0 0 1 0 + ] + egs = Edge.([(1, 2), (2, 3), (3, 4), (4, 5), (2, 1), (3, 2), (4, 3), (5, 4)]) + @test typeof(B) <: SparseMatrixCSC + @test B == B_ + + # Case: simple directed + dg = SimpleDiGraph(5) + add_edge!(dg, 1, 2) + add_edge!(dg, 2, 3) + add_edge!(dg, 1, 3) + add_edge!(dg, 3, 4) + add_edge!(dg, 3, 5) + add_edge!(dg, 4, 3) + B, edgemap = non_backtracking_matrix(dg) + # | 1->2 | 1->3 | 2->3 | 3->4 | 3->5 | 4->3 + # ----------------------------------------------- + # 1->2 | 0 | 0 | 1 | 0 | 0 | 0 + # 1->3 | 0 | 0 | 0 | 1 | 1 | 0 + # 2->3 | 0 | 0 | 0 | 1 | 1 | 0 + # 3->4 | 0 | 0 | 0 | 0 | 0 | 0 + # 3->5 | 0 | 0 | 0 | 0 | 0 | 0 + # 4->3 | 0 | 0 | 0 | 0 | 1 | 0 + B_ = [ + 0 0 1 0 0 0 + 0 0 0 1 1 0 + 0 0 0 1 1 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 1 0 + ] + egs = Edge.([(1, 2), (1, 3), (2, 3), (3, 4), (3, 5), (4, 3)]) + @test typeof(B) <: SparseMatrixCSC + @test B == B_ +end diff --git a/test/linalg/runtests.jl b/test/linalg/runtests.jl index 697e41354..a50394cb2 100644 --- a/test/linalg/runtests.jl +++ b/test/linalg/runtests.jl @@ -4,7 +4,7 @@ using SparseArrays using LinearAlgebra const linalgtestdir = dirname(@__FILE__) -tests = ["graphmatrices", "spectral"] +tests = ["graphmatrices", "spectral", "nonbacktracking"] @testset "Graphs.LinAlg" begin for t in tests