From e6d90c62573f1c9e801ed17f80614fa9198c4dba Mon Sep 17 00:00:00 2001 From: Samuel Esposito <40698384+samuel-esp@users.noreply.github.com> Date: Sun, 12 Sep 2021 14:23:11 +0200 Subject: [PATCH 1/8] new examples added --- examples/AdvancedPrim.jl | 49 ++++++++++++++++++ examples/BFSLevelsMultiSource.jl | 47 ++++++++++++++++++ examples/BFSParents.jl | 74 ++++++++++++++++++++++++++++ examples/BFSParentsMultiSource.jl | 71 ++++++++++++++++++++++++++ examples/BasicPrim.jl | 44 +++++++++++++++++ examples/BellmanFord.jl | 27 ++++++++++ examples/CDLP.jl | 82 +++++++++++++++++++++++++++++++ examples/CMU.jl | 33 +++++++++++++ examples/Cohen.jl | 29 +++++++++++ examples/FloydWarshall.jl | 27 ++++++++++ examples/KTruss.jl | 27 ++++++++++ examples/NodeWiseTriangleCount.jl | 20 ++++++++ examples/Sandia.jl | 24 +++++++++ 13 files changed, 554 insertions(+) create mode 100644 examples/AdvancedPrim.jl create mode 100644 examples/BFSLevelsMultiSource.jl create mode 100644 examples/BFSParents.jl create mode 100644 examples/BFSParentsMultiSource.jl create mode 100644 examples/BasicPrim.jl create mode 100644 examples/BellmanFord.jl create mode 100644 examples/CDLP.jl create mode 100644 examples/CMU.jl create mode 100644 examples/Cohen.jl create mode 100644 examples/FloydWarshall.jl create mode 100644 examples/KTruss.jl create mode 100644 examples/NodeWiseTriangleCount.jl create mode 100644 examples/Sandia.jl diff --git a/examples/AdvancedPrim.jl b/examples/AdvancedPrim.jl new file mode 100644 index 00000000..83eea02d --- /dev/null +++ b/examples/AdvancedPrim.jl @@ -0,0 +1,49 @@ +#= +AdvancedPrim: +- Julia version: +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +function stoppingCondition(m, n) + +infinito = true + +for i = 1:n + if(m[i] == 0.0) + return false + end +end + return infinito +end + +#A is the input matrix, n is the number of nodes, m is the source matrix. Output: minimum spanning tree and minimum spanning tree cost +function a_prim(A, n, m) + + d = GBVector{Float64}(n) + + weight = 0.0 + + d = A[1,:] + mst = GBVector{Float64}(n) #minimum spanning tree + + while(stoppingCondition(m, n) == false) + + u = argmin(m'+d) + m[u[2]] = Inf + push!(mst, d[u[2]]) + weight = weight + d[u[2]] + print("WEIGHT: ") + print(weight) + d = emul(d, A[u[1],:], BinaryOps.MIN) + print(" ITERATION FINISHED") + print("\n\n\n") + end + + return weight, mst + +end \ No newline at end of file diff --git a/examples/BFSLevelsMultiSource.jl b/examples/BFSLevelsMultiSource.jl new file mode 100644 index 00000000..78aeaab9 --- /dev/null +++ b/examples/BFSLevelsMultiSource.jl @@ -0,0 +1,47 @@ +#= +BFSLevelsMultiSource: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +#A is the input matrix, S is the source matrix, and n is the number of nodes, s is the number of sources in the matrix S, Output: Levels matrix +function ms_bfsl(A, S, n, s) + + #this vector allows to check if a value is already present inside the distance matrix, + #if it's present we don't want it to be changed + levels = GBVector{Int64}(n) + for i = 1:n + levels[i] = i + end + + #distance matrix + distance = GBMatrix{Int64}(s, n) + for i = 1:s + for j = 1:n + distance[i, j] = 0 + end + end + + for level = 1:n-1 + + for i = 1:s + for j = 1:n + if(S[i, j]!=0 && distance[i, j] ∉ levels) #* + distance[i, j] = level #* + end + end + end + + + S = mul(S, A, Semirings.LOR_LAND, mask=distance, desc=Descriptors.RC) + + end + + return distance + +end \ No newline at end of file diff --git a/examples/BFSParents.jl b/examples/BFSParents.jl new file mode 100644 index 00000000..c41dbb5c --- /dev/null +++ b/examples/BFSParents.jl @@ -0,0 +1,74 @@ +#= +BFSParents: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +#function to insert inside the result array +function insert(p, result) + for i=1:length(p) + if(p[i]!=nothing && result[i]==nothing) + result[i] = p[i] + end + end + + return result + +end + +#function to update visited nodes +function updateVisited(f, visited) + for i=1:length(f) + if(f[i]!=nothing) + visited[i] = true + end + end + + return visited + +end + +#A is the input matrix, s is the source node, and n is the number of nodes, Output: parent matrix +function bfs_parents(A, s, n) + + index = GBVector{Int64}(n) + for i = 1:n + index[i] = i + end + + #wavefront vector -> w[s]=source node insrted in wavefront + f = GBVector{Int64}(n) + f[s] = 1 + + #parent vector -> p[s]=1 because the source is already visited + p = GBVector{Int64}(n) + + #result vector -> result[s]=0 because the parent of the source is considered node 0 + result = GBVector{Int64}(n) + result[s] = 0 + + #visited vector -> v[s]=1 because the source is already visited + visited = GBVector{Bool}(n) + visited[s] = true + + for i = 1:n-1 + f = mul(f, A, Semirings.MIN_FIRST, mask=p, desc=Descriptors.SC) + p = f[:, mask=f, desc=Descriptors.S] + f = index[:, mask=f, desc=Descriptors.S] + result = insert(p, result) + visited = updateVisited(f, visited) + unvisited = filter(x -> x!=true, visited) + if(isempty(unvisited)) + break + end + + end + + return result + +end \ No newline at end of file diff --git a/examples/BFSParentsMultiSource.jl b/examples/BFSParentsMultiSource.jl new file mode 100644 index 00000000..2f4d1b81 --- /dev/null +++ b/examples/BFSParentsMultiSource.jl @@ -0,0 +1,71 @@ +#= +BFSParentsMultiSource: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +function insert(P, R, s, n) + for i = 1:s + for j = 1:n + if(P[i, j]!=nothing && R[i, j]==nothing) #* + R[i, j] = P[i, j] + end + end + end + + return R + +end + +#A is the input matrix, S are the sources, n is the number of nodes, s is the number of sources, Output: Parents matrix +function ms_bfsp(A, S, n, s) + + index = GBMatrix{Int64}(s, n) + for i = 1:n + for j = 1:s + index[j, i] = i + end + end + + #frontier matrix -> F=S source nodes insrted in frontier + F = GBMatrix{Int64}(n, n) + F = S + + #parent matrix -> P[S]=S because the source is already visited + P = GBMatrix{Int64}(n, n) + P = S + + #result matrix -> R[S]=0 because the parents of the source are considered node 0 + R = GBMatrix{Int64}(s, n) + LR = GBMatrix{Int64}(s, n) + R = copy(S) + for i = 1:n + for j = 1:s + if(R[j, i]!=nothing) + R[j, i] = 0 + end + end + end + + + for i = 1:n-1 + F = mul(F, A, Semirings.MIN_FIRST, mask=P, desc=Descriptors.RC) + P = F[:, :, mask=F, desc=Descriptors.S] + F = index[:, :, mask=F, desc=Descriptors.S] + R = insert(P, R, s, n) + if(R == LR) + break + end + LR = copy(R) + + end + + return R + +end + diff --git a/examples/BasicPrim.jl b/examples/BasicPrim.jl new file mode 100644 index 00000000..aba2ac2d --- /dev/null +++ b/examples/BasicPrim.jl @@ -0,0 +1,44 @@ +#= +BasicPrim: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays + +function stoppingCondition(m, n) + +infinito = true + +for i = 1:n + if(m[i] == 0.0) + return false + end +end + return infinito +end + +#A is the input matrix, n is the number of nodes, m is the source matrix Output: minimum spanning tree cost +function b_prim(A, n, m) + + d = GBVector{Float64}(n) + + weight = 0.0 + + d = A[1,:] + print("INITIAL WEIGHT: ") + print(weight) + print("\n\n") + while(stoppingCondition(m, n) == false) + + u = argmin(m'+d) + m[u[2]] = Inf + weight = weight + d[u[2]] + d = emul(d, A[u[1],:], BinaryOps.MIN) + end + + return weight + +end \ No newline at end of file diff --git a/examples/BellmanFord.jl b/examples/BellmanFord.jl new file mode 100644 index 00000000..4bcc45b6 --- /dev/null +++ b/examples/BellmanFord.jl @@ -0,0 +1,27 @@ +#= +BellmanFord: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays + +#A is the input matrix, s is the source node, and n is the number of nodes, Output: Shortest path from source node +function bellmanford(A, s, n) + + #vector init +inf + d = GBVector{Float64}(n) + for i = 1:n + d[i] = Inf + end + + d[s]=0.0 + for i = 1:n + d = mul(d, A, Semirings.MIN_PLUS, mask=d, desc=Descriptors.S) + end + + return d + +end \ No newline at end of file diff --git a/examples/CDLP.jl b/examples/CDLP.jl new file mode 100644 index 00000000..b13bc3ff --- /dev/null +++ b/examples/CDLP.jl @@ -0,0 +1,82 @@ +#= +CDLP: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra +using DataStructures + +#minMode function: given a collection [1, 5, 5, 3, 3, 7] will return 3 because the highest repetition is 2 +#and the lowest most repeated number is 3 +function minMode(c) + + minVal = -1 + i = 0 + + for row in eachrow(c) + i = i+1 + row = collect(row) + temp = filter(x -> x!=0, row) + count = counter(temp) + sortedCollection = sort(collect(count), by=x->x[2], rev=true) + minVal = sortedCollection[1][1] + repetitions = sortedCollection[1][2] + for (key, value) in sortedCollection + if(value == repetitions) + if(key < minVal) + minVal = key + end + end + end + + end + + return minVal +end + +#can be useful to tweak the algorithm +function diag_conversion(lab, n) +for i = 1:n + for j = 1:n + if(i==j) + lab[i,j]=i + else + lab[i,j]=0 + end + end + end + return lab +end + +#A is the input matrix, n is the number of nodes, t the number of iterations Output: label propagation matrix +function cdlp(A, n, t) + + lab = GBMatrix{Int64}(n,n) + + for i = 1:n + for j = 1:n + if(i==j) + lab[i,j]=i + else + lab[i,j]=0 + end + end + end + + for k = 1:t + F = mul(A, lab, Semirings.PLUS_TIMES) + for i = 1:n + r = F[i,:] + r = sort(r, dims=2) + lab[i, i] = minMode(r) + end + + end + + return lab + +end \ No newline at end of file diff --git a/examples/CMU.jl b/examples/CMU.jl new file mode 100644 index 00000000..7c56b4ac --- /dev/null +++ b/examples/CMU.jl @@ -0,0 +1,33 @@ +#= +CMU: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +#A is the input matrix, n is the number of nodes in the original matrix, Output: Triangle Count +function cmu(A, n) + + t = 0 + + for i = 2:n-1 + + #rpva + A20 = A[i+1:n,begin:i] + a10 = A[begin:i, i] + a12 = A[i, i+1:n] + + partial1 = mul(a10', A20', Semirings.PLUS_TIMES) + partial2 = mul(partial1, a12', Semirings.PLUS_TIMES) + + t = t + partial2[1] + end + + print(t) + + return t +end \ No newline at end of file diff --git a/examples/Cohen.jl b/examples/Cohen.jl new file mode 100644 index 00000000..50d48065 --- /dev/null +++ b/examples/Cohen.jl @@ -0,0 +1,29 @@ +#= +Cohen: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +##A is the input matrix, Output: Triangle Count +function cohen(A) + + #extracting lower matrix from input matrix + L = tril(A) + + + #extracting upper matrix from input matrix + U = triu(A') + + B = mul(L, U, Semirings.PLUS_TIMES) + C = emul(B, A, BinaryOps.TIMES) + result = reduce(Monoids.PLUS_MONOID[Float64], C, dims=:) + result = result /2 + + return result + +end \ No newline at end of file diff --git a/examples/FloydWarshall.jl b/examples/FloydWarshall.jl new file mode 100644 index 00000000..fe084f52 --- /dev/null +++ b/examples/FloydWarshall.jl @@ -0,0 +1,27 @@ +#= +FloydWarshall: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays + +#A is the input matrix for Floyd-Warshall multiple sources multiple destinations, Output: shortest paths +function floydwarshall(A, n) + + #distance matrix = input matrix + D = A + result = GBMatrix{Int64}(n, n) + + + for i = 1:n + partial = mul(D[:,i], D[i,:], Semirings.MIN_PLUS) + D = emul(D, partial, BinaryOps.MIN) + result = partial + end + + return result + +end \ No newline at end of file diff --git a/examples/KTruss.jl b/examples/KTruss.jl new file mode 100644 index 00000000..f705e20b --- /dev/null +++ b/examples/KTruss.jl @@ -0,0 +1,27 @@ +#= +KTruss: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +#A is the original matrix, k is the requested truss, n the nodes in the original matrix, Output: K-Truss graph +function ktruss(A, k, n) + + C = A + for i = 1:n-1 + C = mul(C, C, Semirings.PLUS_LAND, mask=C) + C = select(SelectOps.GE, C, k-2) + print(C) + end + + return C + +end + +#*** +#optional: we could also tell the algorithm to stop if the non zero values didn't change from the previous iteration \ No newline at end of file diff --git a/examples/NodeWiseTriangleCount.jl b/examples/NodeWiseTriangleCount.jl new file mode 100644 index 00000000..bffd0f64 --- /dev/null +++ b/examples/NodeWiseTriangleCount.jl @@ -0,0 +1,20 @@ +#= +NodeWiseTriangleCount: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +#A is the input matrix. Output: triangles originated by each node +function nodewise_tc(A) + + #extracting lower matrix from input matrix + partial = mul(A, A, Semirings.PLUS_TIMES, mask=A) + t = reduce(Monoids.PLUS_MONOID[Float64], partial, dims=2) + return t/2 + +end \ No newline at end of file diff --git a/examples/Sandia.jl b/examples/Sandia.jl new file mode 100644 index 00000000..00a75971 --- /dev/null +++ b/examples/Sandia.jl @@ -0,0 +1,24 @@ +#= +Sandia: +- Julia version: 1.6.2 +- Author: samuel +- Date: 2021-09-10 +=# + +using SuiteSparseGraphBLAS +using SparseArrays +using LinearAlgebra + +#A is the input matrix, Output: Triangle Count +function sandia(A) + + #extracting lower matrix from input matrix + L = tril(A) + + + C = mul(L, L, Semirings.PLUS_TIMES, mask=L) + result = reduce(Monoids.PLUS_MONOID, C, dims=:) + + return result + +end \ No newline at end of file From 2a42a4c49107b49ac376abf3b25464354f892d42 Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Sat, 30 Oct 2021 20:21:31 -0400 Subject: [PATCH 2/8] Some updates to more recent API. --- examples/Sandia.jl | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/examples/Sandia.jl b/examples/Sandia.jl index 00a75971..46d90b98 100644 --- a/examples/Sandia.jl +++ b/examples/Sandia.jl @@ -12,13 +12,8 @@ using LinearAlgebra #A is the input matrix, Output: Triangle Count function sandia(A) - #extracting lower matrix from input matrix L = tril(A) - - - C = mul(L, L, Semirings.PLUS_TIMES, mask=L) - result = reduce(Monoids.PLUS_MONOID, C, dims=:) - - return result + C = *(+, *; mask=L)(L, L) + return reduce(+, C, dims=:) end \ No newline at end of file From 851f6e14f4274c5834725936ac265da77564861b Mon Sep 17 00:00:00 2001 From: Will Kimmerer Date: Sat, 30 Oct 2021 20:24:59 -0400 Subject: [PATCH 3/8] Some more updates --- examples/BFSParentsMultiSource.jl | 8 +++----- examples/BasicPrim.jl | 2 +- examples/BellmanFord.jl | 4 ++-- examples/CDLP.jl | 4 +--- examples/CMU.jl | 27 ++++++++++----------------- examples/Cohen.jl | 18 +++--------------- examples/FloydWarshall.jl | 6 ++---- examples/KTruss.jl | 5 ++--- examples/NodeWiseTriangleCount.jl | 5 ++--- 9 files changed, 26 insertions(+), 53 deletions(-) diff --git a/examples/BFSParentsMultiSource.jl b/examples/BFSParentsMultiSource.jl index 2f4d1b81..f5bbdc18 100644 --- a/examples/BFSParentsMultiSource.jl +++ b/examples/BFSParentsMultiSource.jl @@ -12,7 +12,7 @@ using LinearAlgebra function insert(P, R, s, n) for i = 1:s for j = 1:n - if(P[i, j]!=nothing && R[i, j]==nothing) #* + if(P[i, j] !== nothing && R[i, j] === nothing) #* R[i, j] = P[i, j] end end @@ -51,10 +51,8 @@ function ms_bfsp(A, S, n, s) end end end - - - for i = 1:n-1 - F = mul(F, A, Semirings.MIN_FIRST, mask=P, desc=Descriptors.RC) + for _ ∈ 1:n-1 + F = mul(F, A, (min, first), mask=P, desc=Descriptors.RC) P = F[:, :, mask=F, desc=Descriptors.S] F = index[:, :, mask=F, desc=Descriptors.S] R = insert(P, R, s, n) diff --git a/examples/BasicPrim.jl b/examples/BasicPrim.jl index aba2ac2d..4214f5ea 100644 --- a/examples/BasicPrim.jl +++ b/examples/BasicPrim.jl @@ -36,7 +36,7 @@ function b_prim(A, n, m) u = argmin(m'+d) m[u[2]] = Inf weight = weight + d[u[2]] - d = emul(d, A[u[1],:], BinaryOps.MIN) + d = min.(d, A[u[1],:]) end return weight diff --git a/examples/BellmanFord.jl b/examples/BellmanFord.jl index 4bcc45b6..460995e6 100644 --- a/examples/BellmanFord.jl +++ b/examples/BellmanFord.jl @@ -18,8 +18,8 @@ function bellmanford(A, s, n) end d[s]=0.0 - for i = 1:n - d = mul(d, A, Semirings.MIN_PLUS, mask=d, desc=Descriptors.S) + for _ ∈ 1:n + d = mul(d, A, (min, +), mask=d, desc=Descriptors.S) end return d diff --git a/examples/CDLP.jl b/examples/CDLP.jl index b13bc3ff..e5ad3921 100644 --- a/examples/CDLP.jl +++ b/examples/CDLP.jl @@ -68,15 +68,13 @@ function cdlp(A, n, t) end for k = 1:t - F = mul(A, lab, Semirings.PLUS_TIMES) + F = A * lab for i = 1:n r = F[i,:] r = sort(r, dims=2) lab[i, i] = minMode(r) end - end - return lab end \ No newline at end of file diff --git a/examples/CMU.jl b/examples/CMU.jl index 7c56b4ac..1d6da176 100644 --- a/examples/CMU.jl +++ b/examples/CMU.jl @@ -11,23 +11,16 @@ using LinearAlgebra #A is the input matrix, n is the number of nodes in the original matrix, Output: Triangle Count function cmu(A, n) - - t = 0 - - for i = 2:n-1 - - #rpva - A20 = A[i+1:n,begin:i] - a10 = A[begin:i, i] - a12 = A[i, i+1:n] - - partial1 = mul(a10', A20', Semirings.PLUS_TIMES) - partial2 = mul(partial1, a12', Semirings.PLUS_TIMES) - - t = t + partial2[1] + t = 0 + for i ∈ 2:n-1 + #rpva + A20 = A[i+1:n,begin:i] + a10 = A[begin:i, i] + a12 = A[i, i+1:n] + + partial1 = a10' * A20' + partial2 = partial1 * a12' + t += partial2[1] end - - print(t) - return t end \ No newline at end of file diff --git a/examples/Cohen.jl b/examples/Cohen.jl index 50d48065..b8f112f2 100644 --- a/examples/Cohen.jl +++ b/examples/Cohen.jl @@ -11,19 +11,7 @@ using LinearAlgebra ##A is the input matrix, Output: Triangle Count function cohen(A) - - #extracting lower matrix from input matrix - L = tril(A) - - - #extracting upper matrix from input matrix - U = triu(A') - - B = mul(L, U, Semirings.PLUS_TIMES) - C = emul(B, A, BinaryOps.TIMES) - result = reduce(Monoids.PLUS_MONOID[Float64], C, dims=:) - result = result /2 - - return result - + U = select(TRIU, A) + L = select(TRIL, A) + return reduce(+, mul(L, U, (+, pair); mask=A)) ÷ 2 end \ No newline at end of file diff --git a/examples/FloydWarshall.jl b/examples/FloydWarshall.jl index fe084f52..fde73a78 100644 --- a/examples/FloydWarshall.jl +++ b/examples/FloydWarshall.jl @@ -15,13 +15,11 @@ function floydwarshall(A, n) D = A result = GBMatrix{Int64}(n, n) - for i = 1:n - partial = mul(D[:,i], D[i,:], Semirings.MIN_PLUS) - D = emul(D, partial, BinaryOps.MIN) + partial = mul(D[:,i], D[i,:], (min, +)) + D = emul(D, partial, min) result = partial end - return result end \ No newline at end of file diff --git a/examples/KTruss.jl b/examples/KTruss.jl index f705e20b..39bf5c27 100644 --- a/examples/KTruss.jl +++ b/examples/KTruss.jl @@ -13,12 +13,11 @@ using LinearAlgebra function ktruss(A, k, n) C = A - for i = 1:n-1 + for _ ∈ 1:n-1 C = mul(C, C, Semirings.PLUS_LAND, mask=C) - C = select(SelectOps.GE, C, k-2) + C = select(>=, C, k-2) print(C) end - return C end diff --git a/examples/NodeWiseTriangleCount.jl b/examples/NodeWiseTriangleCount.jl index bffd0f64..2aba3b8f 100644 --- a/examples/NodeWiseTriangleCount.jl +++ b/examples/NodeWiseTriangleCount.jl @@ -12,9 +12,8 @@ using LinearAlgebra #A is the input matrix. Output: triangles originated by each node function nodewise_tc(A) - #extracting lower matrix from input matrix - partial = mul(A, A, Semirings.PLUS_TIMES, mask=A) - t = reduce(Monoids.PLUS_MONOID[Float64], partial, dims=2) + partial = mul(A, A, (+, *), mask=A) + t = reduce(+, partial, dims=2) return t/2 end \ No newline at end of file From ba05a83799dc1544f8c82d50d0c6110e2108f188 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Tue, 27 Oct 2026 15:19:18 +0000 Subject: [PATCH 4/8] Add a reference link to each new example Co-Authored-By: Claude Opus 5.5 --- examples/AdvancedPrim.jl | 1 + examples/BFSLevelsMultiSource.jl | 1 + examples/BFSParents.jl | 1 + examples/BFSParentsMultiSource.jl | 1 + examples/BasicPrim.jl | 1 + examples/BellmanFord.jl | 1 + examples/CDLP.jl | 1 + examples/CMU.jl | 1 + examples/Cohen.jl | 1 + examples/FloydWarshall.jl | 1 + examples/KTruss.jl | 1 + examples/NodeWiseTriangleCount.jl | 1 + examples/Sandia.jl | 1 + 13 files changed, 13 insertions(+) diff --git a/examples/AdvancedPrim.jl b/examples/AdvancedPrim.jl index 83eea02d..4df0717e 100644 --- a/examples/AdvancedPrim.jl +++ b/examples/AdvancedPrim.jl @@ -3,6 +3,7 @@ AdvancedPrim: - Julia version: - Author: samuel - Date: 2021-09-10 +- Reference: https://en.wikipedia.org/wiki/Prim%27s_algorithm =# using SuiteSparseGraphBLAS diff --git a/examples/BFSLevelsMultiSource.jl b/examples/BFSLevelsMultiSource.jl index 78aeaab9..0a545b54 100644 --- a/examples/BFSLevelsMultiSource.jl +++ b/examples/BFSLevelsMultiSource.jl @@ -3,6 +3,7 @@ BFSLevelsMultiSource: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://en.wikipedia.org/wiki/Breadth-first_search =# using SuiteSparseGraphBLAS diff --git a/examples/BFSParents.jl b/examples/BFSParents.jl index c41dbb5c..c129c19a 100644 --- a/examples/BFSParents.jl +++ b/examples/BFSParents.jl @@ -3,6 +3,7 @@ BFSParents: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://en.wikipedia.org/wiki/Breadth-first_search =# using SuiteSparseGraphBLAS diff --git a/examples/BFSParentsMultiSource.jl b/examples/BFSParentsMultiSource.jl index f5bbdc18..e959de8f 100644 --- a/examples/BFSParentsMultiSource.jl +++ b/examples/BFSParentsMultiSource.jl @@ -3,6 +3,7 @@ BFSParentsMultiSource: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://en.wikipedia.org/wiki/Breadth-first_search =# using SuiteSparseGraphBLAS diff --git a/examples/BasicPrim.jl b/examples/BasicPrim.jl index 4214f5ea..e4455e8e 100644 --- a/examples/BasicPrim.jl +++ b/examples/BasicPrim.jl @@ -3,6 +3,7 @@ BasicPrim: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://en.wikipedia.org/wiki/Prim%27s_algorithm =# using SuiteSparseGraphBLAS diff --git a/examples/BellmanFord.jl b/examples/BellmanFord.jl index 460995e6..1cfa6b17 100644 --- a/examples/BellmanFord.jl +++ b/examples/BellmanFord.jl @@ -3,6 +3,7 @@ BellmanFord: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm =# using SuiteSparseGraphBLAS diff --git a/examples/CDLP.jl b/examples/CDLP.jl index e5ad3921..5dc8a0a2 100644 --- a/examples/CDLP.jl +++ b/examples/CDLP.jl @@ -3,6 +3,7 @@ CDLP: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://doi.org/10.1103/PhysRevE.76.036106 (CDLP as defined in LDBC Graphalytics, https://arxiv.org/abs/2011.15028) =# using SuiteSparseGraphBLAS diff --git a/examples/CMU.jl b/examples/CMU.jl index 1d6da176..cb8dec54 100644 --- a/examples/CMU.jl +++ b/examples/CMU.jl @@ -3,6 +3,7 @@ CMU: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://doi.org/10.1109/HPEC.2017.8091046 =# using SuiteSparseGraphBLAS diff --git a/examples/Cohen.jl b/examples/Cohen.jl index b8f112f2..af52a1a3 100644 --- a/examples/Cohen.jl +++ b/examples/Cohen.jl @@ -3,6 +3,7 @@ Cohen: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://doi.org/10.1109/MCSE.2009.120, https://doi.org/10.1109/IPDPSW.2015.75 =# using SuiteSparseGraphBLAS diff --git a/examples/FloydWarshall.jl b/examples/FloydWarshall.jl index fde73a78..e031f3f5 100644 --- a/examples/FloydWarshall.jl +++ b/examples/FloydWarshall.jl @@ -3,6 +3,7 @@ FloydWarshall: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm =# using SuiteSparseGraphBLAS diff --git a/examples/KTruss.jl b/examples/KTruss.jl index 39bf5c27..8944da53 100644 --- a/examples/KTruss.jl +++ b/examples/KTruss.jl @@ -3,6 +3,7 @@ KTruss: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://doi.org/10.1109/HPEC.2018.8547538 =# using SuiteSparseGraphBLAS diff --git a/examples/NodeWiseTriangleCount.jl b/examples/NodeWiseTriangleCount.jl index 2aba3b8f..baffde85 100644 --- a/examples/NodeWiseTriangleCount.jl +++ b/examples/NodeWiseTriangleCount.jl @@ -3,6 +3,7 @@ NodeWiseTriangleCount: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://doi.org/10.1109/HPEC.2018.8547538 =# using SuiteSparseGraphBLAS diff --git a/examples/Sandia.jl b/examples/Sandia.jl index 46d90b98..dee2887b 100644 --- a/examples/Sandia.jl +++ b/examples/Sandia.jl @@ -3,6 +3,7 @@ Sandia: - Julia version: 1.6.2 - Author: samuel - Date: 2021-09-10 +- Reference: https://doi.org/10.1109/HPEC.2017.8091043 =# using SuiteSparseGraphBLAS From 27dedff8979796c79f908fe862b07ddca1c8a8aa Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Tue, 27 Oct 2026 15:25:20 +0000 Subject: [PATCH 5/8] Port the new examples to the current API and fix bugs found by testing - Use `*(A, B, op; mask)`, `Structural`/`Complement` masks and `Descriptor(...)` in place of the removed `mul`, `Semirings.*`, `Descriptors.*` and `TRIU`/`TRIL` APIs; drop debug prints. - BFS: use `fill=nothing` so missing entries read as `nothing`, seed the frontier with the source index rather than 1 (parents were wrong for any source other than 1), and replace `filter` on a GBVector. - Prim: `argmin` now returns a linear index; the old code relaxed with row `u[1]` (always 1) instead of the selected vertex. - Floyd-Warshall: combine with `eadd` (union) instead of `emul`, and return the distance matrix rather than the last outer product. - K-truss: iterate until the edge count stops changing. - CDLP: drop the DataStructures dependency and compute the mode of the neighbour labels directly. Co-Authored-By: Claude Opus 5.5 --- examples/AdvancedPrim.jl | 19 ++++++++----------- examples/BFSLevelsMultiSource.jl | 5 +++-- examples/BFSParents.jl | 22 +++++++++++----------- examples/BFSParentsMultiSource.jl | 22 ++++++++++------------ examples/BasicPrim.jl | 14 ++++++-------- examples/BellmanFord.jl | 5 +++-- examples/CDLP.jl | 31 +++++++------------------------ examples/Cohen.jl | 7 ++++--- examples/FloydWarshall.jl | 11 +++++------ examples/KTruss.jl | 18 ++++++++---------- examples/NodeWiseTriangleCount.jl | 4 ++-- examples/Sandia.jl | 2 +- 12 files changed, 68 insertions(+), 92 deletions(-) diff --git a/examples/AdvancedPrim.jl b/examples/AdvancedPrim.jl index 4df0717e..8001f5c1 100644 --- a/examples/AdvancedPrim.jl +++ b/examples/AdvancedPrim.jl @@ -22,7 +22,8 @@ end return infinito end -#A is the input matrix, n is the number of nodes, m is the source matrix. Output: minimum spanning tree and minimum spanning tree cost +#A is the input matrix (dense, Inf where there is no edge and zeros on the diagonal), n is the number of nodes, +#m is a vector of n zeros (Inf marks nodes already in the tree). Output: minimum spanning tree cost and edge weights in the order they were added function a_prim(A, n, m) d = GBVector{Float64}(n) @@ -30,19 +31,15 @@ function a_prim(A, n, m) weight = 0.0 d = A[1,:] - mst = GBVector{Float64}(n) #minimum spanning tree + mst = Float64[] #minimum spanning tree while(stoppingCondition(m, n) == false) - u = argmin(m'+d) - m[u[2]] = Inf - push!(mst, d[u[2]]) - weight = weight + d[u[2]] - print("WEIGHT: ") - print(weight) - d = emul(d, A[u[1],:], BinaryOps.MIN) - print(" ITERATION FINISHED") - print("\n\n\n") + u = argmin(m+d) + m[u] = Inf + push!(mst, d[u]) + weight = weight + d[u] + d = emul(d, A[u,:], min) end return weight, mst diff --git a/examples/BFSLevelsMultiSource.jl b/examples/BFSLevelsMultiSource.jl index 0a545b54..b44d07b4 100644 --- a/examples/BFSLevelsMultiSource.jl +++ b/examples/BFSLevelsMultiSource.jl @@ -9,6 +9,7 @@ BFSLevelsMultiSource: using SuiteSparseGraphBLAS using SparseArrays using LinearAlgebra +using SuiteSparseGraphBLAS: Complement, ∨, ∧ #A is the input matrix, S is the source matrix, and n is the number of nodes, s is the number of sources in the matrix S, Output: Levels matrix function ms_bfsl(A, S, n, s) @@ -32,14 +33,14 @@ function ms_bfsl(A, S, n, s) for i = 1:s for j = 1:n - if(S[i, j]!=0 && distance[i, j] ∉ levels) #* + if(S[i, j] !== nothing && S[i, j] != 0 && distance[i, j] ∉ levels) #* distance[i, j] = level #* end end end - S = mul(S, A, Semirings.LOR_LAND, mask=distance, desc=Descriptors.RC) + S = *(S, A, (∨, ∧), mask=Complement(distance), desc=Descriptor(replace_output=true)) end diff --git a/examples/BFSParents.jl b/examples/BFSParents.jl index c129c19a..3a27d434 100644 --- a/examples/BFSParents.jl +++ b/examples/BFSParents.jl @@ -9,6 +9,7 @@ BFSParents: using SuiteSparseGraphBLAS using SparseArrays using LinearAlgebra +using SuiteSparseGraphBLAS: Structural, Complement, second #function to insert inside the result array function insert(p, result) @@ -37,34 +38,33 @@ end #A is the input matrix, s is the source node, and n is the number of nodes, Output: parent matrix function bfs_parents(A, s, n) - index = GBVector{Int64}(n) + index = GBVector{Int64}(n; fill=nothing) for i = 1:n index[i] = i end #wavefront vector -> w[s]=source node insrted in wavefront - f = GBVector{Int64}(n) - f[s] = 1 + f = GBVector{Int64}(n; fill=nothing) + f[s] = s #parent vector -> p[s]=1 because the source is already visited - p = GBVector{Int64}(n) + p = GBVector{Int64}(n; fill=nothing) #result vector -> result[s]=0 because the parent of the source is considered node 0 - result = GBVector{Int64}(n) + result = GBVector{Int64}(n; fill=nothing) result[s] = 0 #visited vector -> v[s]=1 because the source is already visited - visited = GBVector{Bool}(n) + visited = GBVector{Bool}(n; fill=nothing) visited[s] = true for i = 1:n-1 - f = mul(f, A, Semirings.MIN_FIRST, mask=p, desc=Descriptors.SC) - p = f[:, mask=f, desc=Descriptors.S] - f = index[:, mask=f, desc=Descriptors.S] + f = *(A', f, (min, second), mask=Complement(Structural(p))) + p = copy(f) + f = mask(index, Structural(f)) result = insert(p, result) visited = updateVisited(f, visited) - unvisited = filter(x -> x!=true, visited) - if(isempty(unvisited)) + if(nnz(visited) == n) break end diff --git a/examples/BFSParentsMultiSource.jl b/examples/BFSParentsMultiSource.jl index e959de8f..706e1d21 100644 --- a/examples/BFSParentsMultiSource.jl +++ b/examples/BFSParentsMultiSource.jl @@ -9,6 +9,7 @@ BFSParentsMultiSource: using SuiteSparseGraphBLAS using SparseArrays using LinearAlgebra +using SuiteSparseGraphBLAS: Structural, Complement function insert(P, R, s, n) for i = 1:s @@ -26,7 +27,7 @@ end #A is the input matrix, S are the sources, n is the number of nodes, s is the number of sources, Output: Parents matrix function ms_bfsp(A, S, n, s) - index = GBMatrix{Int64}(s, n) + index = GBMatrix{Int64}(s, n; fill=nothing) for i = 1:n for j = 1:s index[j, i] = i @@ -34,28 +35,25 @@ function ms_bfsp(A, S, n, s) end #frontier matrix -> F=S source nodes insrted in frontier - F = GBMatrix{Int64}(n, n) - F = S + F = mask(index, Structural(S)) #parent matrix -> P[S]=S because the source is already visited - P = GBMatrix{Int64}(n, n) - P = S + P = F #result matrix -> R[S]=0 because the parents of the source are considered node 0 - R = GBMatrix{Int64}(s, n) - LR = GBMatrix{Int64}(s, n) - R = copy(S) + R = GBMatrix{Int64}(s, n; fill=nothing) + LR = GBMatrix{Int64}(s, n; fill=nothing) for i = 1:n for j = 1:s - if(R[j, i]!=nothing) + if(S[j, i] !== nothing && S[j, i] != 0) R[j, i] = 0 end end end for _ ∈ 1:n-1 - F = mul(F, A, (min, first), mask=P, desc=Descriptors.RC) - P = F[:, :, mask=F, desc=Descriptors.S] - F = index[:, :, mask=F, desc=Descriptors.S] + F = *(F, A, (min, first), mask=Complement(P)) + P = copy(F) + F = mask(index, Structural(F)) R = insert(P, R, s, n) if(R == LR) break diff --git a/examples/BasicPrim.jl b/examples/BasicPrim.jl index e4455e8e..65ce2f01 100644 --- a/examples/BasicPrim.jl +++ b/examples/BasicPrim.jl @@ -21,7 +21,8 @@ end return infinito end -#A is the input matrix, n is the number of nodes, m is the source matrix Output: minimum spanning tree cost +#A is the input matrix (dense, Inf where there is no edge and zeros on the diagonal), n is the number of nodes, +#m is a vector of n zeros (Inf marks nodes already in the tree) Output: minimum spanning tree cost function b_prim(A, n, m) d = GBVector{Float64}(n) @@ -29,15 +30,12 @@ function b_prim(A, n, m) weight = 0.0 d = A[1,:] - print("INITIAL WEIGHT: ") - print(weight) - print("\n\n") while(stoppingCondition(m, n) == false) - u = argmin(m'+d) - m[u[2]] = Inf - weight = weight + d[u[2]] - d = min.(d, A[u[1],:]) + u = argmin(m+d) + m[u] = Inf + weight = weight + d[u] + d = min.(d, A[u,:]) end return weight diff --git a/examples/BellmanFord.jl b/examples/BellmanFord.jl index 1cfa6b17..8f56c50f 100644 --- a/examples/BellmanFord.jl +++ b/examples/BellmanFord.jl @@ -9,7 +9,8 @@ BellmanFord: using SuiteSparseGraphBLAS using SparseArrays -#A is the input matrix, s is the source node, and n is the number of nodes, Output: Shortest path from source node +#A is the input matrix (A[i,j] is the weight of edge i->j, with explicit zeros on the diagonal), +#s is the source node, and n is the number of nodes, Output: Shortest path from source node function bellmanford(A, s, n) #vector init +inf @@ -20,7 +21,7 @@ function bellmanford(A, s, n) d[s]=0.0 for _ ∈ 1:n - d = mul(d, A, (min, +), mask=d, desc=Descriptors.S) + d = *(A', d, (min, +)) end return d diff --git a/examples/CDLP.jl b/examples/CDLP.jl index 5dc8a0a2..788a08fa 100644 --- a/examples/CDLP.jl +++ b/examples/CDLP.jl @@ -9,34 +9,18 @@ CDLP: using SuiteSparseGraphBLAS using SparseArrays using LinearAlgebra -using DataStructures #minMode function: given a collection [1, 5, 5, 3, 3, 7] will return 3 because the highest repetition is 2 #and the lowest most repeated number is 3 function minMode(c) - minVal = -1 - i = 0 - - for row in eachrow(c) - i = i+1 - row = collect(row) - temp = filter(x -> x!=0, row) - count = counter(temp) - sortedCollection = sort(collect(count), by=x->x[2], rev=true) - minVal = sortedCollection[1][1] - repetitions = sortedCollection[1][2] - for (key, value) in sortedCollection - if(value == repetitions) - if(key < minVal) - minVal = key - end - end + temp = filter(x -> x!=0, c) + count = Dict{eltype(temp), Int}() + for x in temp + count[x] = get(count, x, 0) + 1 end - - end - - return minVal + repetitions = maximum(values(count)) + return minimum(key for (key, value) in count if value == repetitions) end #can be useful to tweak the algorithm @@ -71,8 +55,7 @@ function cdlp(A, n, t) for k = 1:t F = A * lab for i = 1:n - r = F[i,:] - r = sort(r, dims=2) + r = nonzeros(F[i,:]) lab[i, i] = minMode(r) end end diff --git a/examples/Cohen.jl b/examples/Cohen.jl index af52a1a3..c692dd0d 100644 --- a/examples/Cohen.jl +++ b/examples/Cohen.jl @@ -9,10 +9,11 @@ Cohen: using SuiteSparseGraphBLAS using SparseArrays using LinearAlgebra +using SuiteSparseGraphBLAS: pair ##A is the input matrix, Output: Triangle Count function cohen(A) - U = select(TRIU, A) - L = select(TRIL, A) - return reduce(+, mul(L, U, (+, pair); mask=A)) ÷ 2 + U = triu(A) + L = tril(A) + return reduce(+, *(L, U, (+, pair); mask=A)) ÷ 2 end \ No newline at end of file diff --git a/examples/FloydWarshall.jl b/examples/FloydWarshall.jl index e031f3f5..d6f6405c 100644 --- a/examples/FloydWarshall.jl +++ b/examples/FloydWarshall.jl @@ -9,18 +9,17 @@ FloydWarshall: using SuiteSparseGraphBLAS using SparseArrays -#A is the input matrix for Floyd-Warshall multiple sources multiple destinations, Output: shortest paths +#A is the input matrix for Floyd-Warshall multiple sources multiple destinations +#(A[i,j] is the weight of edge i->j, with explicit zeros on the diagonal), Output: shortest paths function floydwarshall(A, n) #distance matrix = input matrix D = A - result = GBMatrix{Int64}(n, n) for i = 1:n - partial = mul(D[:,i], D[i,:], (min, +)) - D = emul(D, partial, min) - result = partial + partial = *(D[:, i:i], D[i:i, :], (min, +)) + D = eadd(D, partial, min) end - return result + return D end \ No newline at end of file diff --git a/examples/KTruss.jl b/examples/KTruss.jl index 8944da53..f33b28a9 100644 --- a/examples/KTruss.jl +++ b/examples/KTruss.jl @@ -9,19 +9,17 @@ KTruss: using SuiteSparseGraphBLAS using SparseArrays using LinearAlgebra +using SuiteSparseGraphBLAS: ∧ -#A is the original matrix, k is the requested truss, n the nodes in the original matrix, Output: K-Truss graph -function ktruss(A, k, n) +#A is the original matrix, k is the requested truss, Output: K-Truss graph (values are edge supports) +function ktruss(A, k) C = A - for _ ∈ 1:n-1 - C = mul(C, C, Semirings.PLUS_LAND, mask=C) + while true + nz = nnz(C) + C = *(C, C, (+, ∧), mask=C) C = select(>=, C, k-2) - print(C) + nnz(C) == nz && return C end - return C -end - -#*** -#optional: we could also tell the algorithm to stop if the non zero values didn't change from the previous iteration \ No newline at end of file +end \ No newline at end of file diff --git a/examples/NodeWiseTriangleCount.jl b/examples/NodeWiseTriangleCount.jl index baffde85..68d905f3 100644 --- a/examples/NodeWiseTriangleCount.jl +++ b/examples/NodeWiseTriangleCount.jl @@ -13,8 +13,8 @@ using LinearAlgebra #A is the input matrix. Output: triangles originated by each node function nodewise_tc(A) - partial = mul(A, A, (+, *), mask=A) + partial = *(A, A, (+, *), mask=A) t = reduce(+, partial, dims=2) - return t/2 + return t .÷ 2 end \ No newline at end of file diff --git a/examples/Sandia.jl b/examples/Sandia.jl index dee2887b..0daeabdf 100644 --- a/examples/Sandia.jl +++ b/examples/Sandia.jl @@ -14,7 +14,7 @@ using LinearAlgebra function sandia(A) L = tril(A) - C = *(+, *; mask=L)(L, L) + C = *(L, L, (+, *); mask=L) return reduce(+, C, dims=:) end \ No newline at end of file From edacd3a801ea0354f678ce892b11c319145e5b9f Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Tue, 27 Oct 2026 15:25:20 +0000 Subject: [PATCH 6/8] Add tests for the new examples Co-Authored-By: Claude Opus 5.5 --- test/examples.jl | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + 2 files changed, 63 insertions(+) create mode 100644 test/examples.jl diff --git a/test/examples.jl b/test/examples.jl new file mode 100644 index 00000000..730aacc4 --- /dev/null +++ b/test/examples.jl @@ -0,0 +1,62 @@ +module ExamplesTest +using SuiteSparseGraphBLAS, SparseArrays, LinearAlgebra, Test + +for f in ["AdvancedPrim", "BasicPrim", "BellmanFord", "BFSLevelsMultiSource", "BFSParents", + "BFSParentsMultiSource", "CDLP", "CMU", "Cohen", "FloydWarshall", "KTruss", + "NodeWiseTriangleCount", "Sandia"] + include(joinpath(@__DIR__, "..", "examples", "$f.jl")) +end + +# Undirected graph with edges 1-2, 1-3, 2-3, 2-4, 3-4, 4-5 and triangles {1,2,3}, {2,3,4}. +I = [1, 1, 2, 2, 3, 4]; J = [2, 3, 3, 4, 4, 5] +sym(v) = sparse([I; J], [J; I], [v; v], 5, 5) +A = GBMatrix(sym(ones(Int, 6))) +w = [1.0, 4.0, 2.0, 7.0, 3.0, 1.0] +# Dense weights: Inf where there is no edge, zeros on the diagonal. +W = fill(Inf, 5, 5); W[diagind(W)] .= 0 +for (i, j, x) in zip(I, J, w); W[i, j] = W[j, i] = x; end + +@testset "triangle counting" begin + @test cohen(A) == sandia(A) == cmu(A, 5) == 2 + @test Vector(nodewise_tc(A)) == [1, 2, 2, 1, 0] + # K4 on 1:4 is the 4-truss of K4 plus the pendant edge 4-5. + K = GBMatrix(sparse([1, 1, 1, 2, 2, 3, 4], [2, 3, 4, 3, 4, 4, 5], 1, 5, 5)) + K = K + K' + @test nnz(ktruss(A, 3)) == 10 && ktruss(A, 3)[4, 5] == 0 + @test nnz(ktruss(K, 4)) == 12 && nnz(ktruss(K, 5)) == 0 +end + +@testset "shortest paths" begin + D = copy(W) + for k in 1:5, i in 1:5, j in 1:5 + D[i, j] = min(D[i, j], D[i, k] + D[k, j]) + end + G = GBMatrix(sparse([I; J; 1:5], [J; I; 1:5], [w; w; zeros(5)])) + @test Matrix(floydwarshall(G, 5)) == D + @test Vector(bellmanford(G, 1, 5)) == D[1, :] == [0, 1, 3, 6, 7] + @test Vector(bellmanford(G, 5, 5)) == D[5, :] +end + +@testset "Prim" begin + @test b_prim(GBMatrix(W), 5, zeros(5)) == 7 + @test a_prim(GBMatrix(W), 5, zeros(5)) == (7, [0, 1, 2, 3, 1]) +end + +@testset "BFS" begin + @test collect(bfs_parents(A, 1, 5)) == [0, 1, 1, 2, 4] + @test collect(bfs_parents(A, 5, 5)) == [2, 4, 4, 5, 0] + S = GBMatrix(sparse([1, 2], [1, 5], 1, 2, 5)) + @test collect(ms_bfsp(A, S, 5, 2)) == [0 1 1 2 4; 2 4 4 5 0] + B = GBMatrix(sym(trues(6))) + @test Matrix(ms_bfsl(B, GBMatrix(sparse([1, 2], [1, 5], true, 2, 5)), 5, 2)) == + [1 2 2 3 4; 4 3 3 2 1] +end + +@testset "CDLP" begin + # Two disjoint triangles converge to the smallest label in each. + T = GBMatrix(sparse([1, 1, 2, 4, 4, 5], [2, 3, 3, 5, 6, 6], 1, 6, 6)) + T = T + T' + @test diag(Matrix(cdlp(T, 6, 2))) == [1, 1, 1, 4, 4, 4] + @test diag(Matrix(cdlp(A, 5, 1))) == [2, 1, 1, 2, 4] +end +end diff --git a/test/runtests.jl b/test/runtests.jl index 3b044e20..4d50a992 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -85,4 +85,5 @@ println("$(SuiteSparseGraphBLAS.get_lib())") include_test("solvers/umfpack.jl") include_test("solvers/cholmod.jl") include_test("serialization.jl") + include_test("examples.jl") end From 312c988221c20e8737cf90818ee3954138e7f64a Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Tue, 27 Oct 2026 15:36:43 +0000 Subject: [PATCH 7/8] Convert the docs examples into doctests Turn the `@repl` blocks in index.md, arrays.md, unaryops.md and binaryops.md into `jldoctest` blocks with outputs generated by `doctest(...; fix=true)`. Random inputs in arrays.md are replaced with small deterministic ones. Printed memory sizes and operator pointer addresses are filtered since they vary across runs and platforms. Co-Authored-By: Claude Opus 5.5 --- docs/src/arrays.md | 162 +++++++++++++++++++++++++++++----- docs/src/binaryops.md | 85 +++++++++++++++--- docs/src/index.md | 200 ++++++++++++++++++++++++++++++++---------- docs/src/unaryops.md | 44 ++++++++-- 4 files changed, 398 insertions(+), 93 deletions(-) diff --git a/docs/src/arrays.md b/docs/src/arrays.md index 5ec56be8..614f54ac 100644 --- a/docs/src/arrays.md +++ b/docs/src/arrays.md @@ -1,5 +1,10 @@ # Array Types +```@meta +DocTestSetup = :(using SuiteSparseGraphBLAS, SparseArrays) +DocTestFilters = [r"memory: \d+ bytes"] +``` + There are two primary array types in SuiteSparseGraphBLAS.jl: [`GBVector`](@ref) and [`GBMatrix`](@ref), as well as a few specialized versions of those array types. The full type hierarchy is: ``` @@ -33,17 +38,38 @@ Users should rarely need to directly interact with the underlying storage format ### Construction There are several methods to construct GBArrays. Shown here are empty construction, conversion from a dense matrix and a sparse matrix, and coordinate form with uniform or *iso* coefficients. -```@setup mat -using SuiteSparseGraphBLAS -using SparseArrays -``` -```@repl mat -x = GBMatrix{Bool}(20_000_000, 50_000) -x = GBMatrix([[1,2] [3,4]]) -x = GBMatrix(sprand(100, 100, 0.5); fill = 0.0) -x = GBMatrix( - rand(1:50_000, 5000), rand(1:500_000, 5000), 1, 500_000, 500_000 -) +```jldoctest mat +julia> x = GBMatrix{Bool}(20_000_000, 50_000) +20000000x50000 GraphBLAS bool matrix, hypersparse by row, ints: 32/32/32 + no entries, memory: 256 bytes + +julia> x = GBMatrix([[1,2] [3,4]]) +2x2 GraphBLAS int64_t matrix, full by col + 4 entries, memory: 272 bytes + + (1,1) 1 + (2,1) 2 + (1,2) 3 + (2,2) 4 + +julia> x = GBMatrix(sparse([1, 2, 3], [3, 1, 2], [1.0, 2.0, 3.0]); fill = 0.0) +3x3 GraphBLAS double matrix, bitmap by col + 3 entries, memory: 321 bytes + + (2,1) 2 + (3,2) 3 + (1,3) 1 + +julia> x = GBMatrix( + [1, 20_000, 400_000], [7, 30_000, 500_000], 1, 500_000, 500_000 + ) +500000x500000 GraphBLAS int64_t matrix, hypersparse by row, ints: 32/32/32 + 3 entries, memory: 288 bytes + iso value: 1 + + (1,7) 1 + (20000,30000) 1 + (400000,500000) 1 ``` ```@docs @@ -56,10 +82,26 @@ A `GBVector` is the one-dimensional equivalent of the `GBMatrix`, and internally ### Construction -```@repl mat -v = GBVector{ComplexF32}(100) -v = GBMatrix(rand(ComplexF64, 3); fill = nothing) -v = GBVector(sprand(Bool, 100_000_000, 0.001)) +```jldoctest mat +julia> v = GBVector{ComplexF32}(100) +100x1 GraphBLAS float complex matrix, sparse by col, ints: 32/32/32 + no entries, memory: 248 bytes + +julia> v = GBMatrix(ComplexF64[1, 2im, 3 + 4im]; fill = nothing) +3x1 GraphBLAS double complex matrix, full by col + 3 entries, memory: 288 bytes + + (1,1) 1 + 0i + (2,1) 0 + 2i + (3,1) 3 + 4i + +julia> v = GBVector(sparsevec([1, 50_000_000, 100_000_000], true, 100_000_000)) +100000000x1 GraphBLAS bool matrix, sparse by col, ints: 64/64/64 + 3 entries, memory: 283 bytes + + (1,1) 1 + (50000000,1) 1 + (100000000,1) 1 ``` ```@docs @@ -81,15 +123,87 @@ The usual AbstractArray and SparseArray indexing capabilities are available. Inc However, many functions outside of GraphBLAS will throw an error if they receive `nothing` from an indexing operation. To accomodate these functions the user may set the fill value for an `AbstractGBArray` on construction and with [`setfill`](@ref) and [`setfill!`](@ref). -```@repl mat -A = GBMatrix([1,1,2,2,3,4,4,5,6,7,7,7], [2,4,5,7,6,1,3,6,3,3,4,5], [1:12...]) -SparseMatrixCSC(A) -A[4] -A[1,2] -A[[1,3,5,7], :] -A[1:2:7, :] -A[:,:] -A[:, 5] +```jldoctest mat +julia> A = GBMatrix([1,1,2,2,3,4,4,5,6,7,7,7], [2,4,5,7,6,1,3,6,3,3,4,5], [1:12...]) +7x7 GraphBLAS int64_t matrix, bitmap by row + 12 entries, memory: 681 bytes + + (1,2) 1 + (1,4) 2 + (2,5) 3 + (2,7) 4 + (3,6) 5 + (4,1) 6 + (4,3) 7 + (5,6) 8 + (6,3) 9 + (7,3) 10 + (7,4) 11 + (7,5) 12 + +julia> SparseMatrixCSC(A) +7×7 SparseMatrixCSC{Int64, Int64} with 12 stored entries: + ⋅ 1 ⋅ 2 ⋅ ⋅ ⋅ + ⋅ ⋅ ⋅ ⋅ 3 ⋅ 4 + ⋅ ⋅ ⋅ ⋅ ⋅ 5 ⋅ + 6 ⋅ 7 ⋅ ⋅ ⋅ ⋅ + ⋅ ⋅ ⋅ ⋅ ⋅ 8 ⋅ + ⋅ ⋅ 9 ⋅ ⋅ ⋅ ⋅ + ⋅ ⋅ 10 11 12 ⋅ ⋅ + +julia> A[4] +6 + +julia> A[1,2] +1 + +julia> A[[1,3,5,7], :] +4x7 GraphBLAS int64_t matrix, bitmap by row + 7 entries, memory: 492 bytes + + (1,2) 1 + (1,4) 2 + (2,6) 5 + (3,6) 8 + (4,3) 10 + (4,4) 11 + (4,5) 12 + +julia> A[1:2:7, :] +4x7 GraphBLAS int64_t matrix, bitmap by row + 7 entries, memory: 492 bytes + + (1,2) 1 + (1,4) 2 + (2,6) 5 + (3,6) 8 + (4,3) 10 + (4,4) 11 + (4,5) 12 + +julia> A[:,:] +7x7 GraphBLAS int64_t matrix, bitmap by row + 12 entries, memory: 681 bytes + + (1,2) 1 + (1,4) 2 + (2,5) 3 + (2,7) 4 + (3,6) 5 + (4,1) 6 + (4,3) 7 + (5,6) 8 + (6,3) 9 + (7,3) 10 + (7,4) 11 + (7,5) 12 + +julia> A[:, 5] +7x1 GraphBLAS int64_t matrix, bitmap by col + 2 entries, memory: 304 bytes + + (2,1) 3 + (7,1) 12 ``` The functionality illustrated above extends to `GBVector` as well. diff --git a/docs/src/binaryops.md b/docs/src/binaryops.md index 759e2033..e13c6b64 100644 --- a/docs/src/binaryops.md +++ b/docs/src/binaryops.md @@ -1,5 +1,10 @@ # Binary Operators +```@meta +DocTestSetup = :(using SuiteSparseGraphBLAS) +DocTestFilters = [r"memory: \d+ bytes", r"Ptr{.*}\(0x[0-9a-f]+\)"] +``` + Binary operators are defined on three domains $D_1 \times D_2 \rightarrow D_3$. However, the vast majority of binary operators are defined on a single domain. @@ -7,29 +12,83 @@ However, the vast majority of binary operators are defined on a single domain. In almost all cases you should pass Julia functions, which will be mapped to built-in operators, or used to create a new user-defined operator. -```@repl -using SuiteSparseGraphBLAS +```jldoctest +julia> x = GBMatrix([[1,2] [3,4]]) +2x2 GraphBLAS int64_t matrix, full by col + 4 entries, memory: 272 bytes + + (1,1) 1 + (2,1) 2 + (1,2) 3 + (2,2) 4 + +julia> x .+ x +2x2 GraphBLAS int64_t matrix, full by row + 4 entries, memory: 272 bytes + + (1,1) 2 + (1,2) 6 + (2,1) 4 + (2,2) 8 + +julia> eadd(x, x, +) +2x2 GraphBLAS int64_t matrix, full by row + 4 entries, memory: 272 bytes + + (1,1) 2 + (1,2) 6 + (2,1) 4 + (2,2) 8 + +julia> x .^ x +2x2 GraphBLAS int64_t matrix, full by row + 4 entries, memory: 272 bytes + + (1,1) 1 + (1,2) 27 + (2,1) 4 + (2,2) 256 + +julia> emul(x, x, ^) +2x2 GraphBLAS int64_t matrix, full by row + 4 entries, memory: 272 bytes + + (1,1) 1 + (1,2) 27 + (2,1) 4 + (2,2) 256 -x = GBMatrix([[1,2] [3,4]]) +julia> x2 = Float64.(x) +2x2 GraphBLAS double matrix, full by col + 4 entries, memory: 272 bytes -x .+ x -eadd(x, x, +) + (1,1) 1 + (2,1) 2 + (1,2) 3 + (2,2) 4 -x .^ x -emul(x, x, ^) +julia> eadd!(x2, x, x, +; accum=/) +2x2 GraphBLAS double matrix, full by col + 4 entries, memory: 272 bytes -x2 = Float64.(x) -eadd!(x2, x, x, +; accum=/) + (1,1) 0.5 + (2,1) 0.5 + (1,2) 0.5 + (2,2) 0.5 ``` Internally functions are lowered like this: -```@repl -using SuiteSparseGraphBLAS +```jldoctest +julia> typedop = binaryop(+, Int64, Int64) +SuiteSparseGraphBLAS.TypedBinaryOperator{typeof(+), Int64, Int64, Int64}(true, true, "GrB_PLUS_INT64", Ptr{SuiteSparseGraphBLAS.LibGraphBLAS.GB_BinaryOp_opaque}(0x0000736051461e20), +, nothing) -typedop = binaryop(+, Int64, Int64) +julia> eadd(GBVector([1,2]), GBVector([3,4]), typedop) +2x1 GraphBLAS int64_t matrix, full by col + 2 entries, memory: 256 bytes -eadd(GBVector([1,2]), GBVector([3,4]), typedop) + (1,1) 4 + (2,1) 6 ``` ## Built-Ins diff --git a/docs/src/index.md b/docs/src/index.md index 59f04712..ca3425e4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,5 +1,10 @@ # SuiteSparseGraphBLAS.jl +```@meta +DocTestSetup = :(using SuiteSparseGraphBLAS) +DocTestFilters = [r"memory: \d+ bytes"] +``` + Fast sparse linear algebra is an essential part of the scientific computing toolkit. Outside of the usual applications, like differential equations, sparse linear algebra provides an elegant way to express graph algorithms on adjacency and incidence matrices. The GraphBLAS standard specifies a set of operations for computing sparse matrix graph algorithm in a vein similar to the BLAS or LAPACK standards. SuiteSparseGraphBLAS.jl is a blazing fast package for shared memory sparse matrix operations which wraps Tim Davis' SuiteSparse:GraphBLAS. If you use this package in your research please see [Citing](@ref). @@ -41,21 +46,21 @@ The core SuiteSparseGraphBLAS.jl array types are `GBVector` and `GBMatrix` which !!! note "GBArray" These docs will often refer to the `GBArray` type, which is the union of `AbstractGBVector`, `AbstractGBMatrix` and their lazy Transpose objects. -```@setup intro -using SuiteSparseGraphBLAS -using SparseArrays -``` - -```@repl intro -# create a size 13 empty sparse vector with Float64 elements. -v = GBVector{Float64}(13) - -# create a 1000 x 1000 empty sparse matrix with ComplexF64 elements. -A = GBMatrix{ComplexF64}(1000, 1000) - -# Non-stored values are equal to the fill value of A, -# which is by default zero(eltype(A)) -A[1,5] == getfill(A) +```jldoctest intro +julia> # create a size 13 empty sparse vector with Float64 elements. + v = GBVector{Float64}(13) +13x1 GraphBLAS double matrix, sparse by col, ints: 32/32/32 + no entries, memory: 248 bytes + +julia> # create a 1000 x 1000 empty sparse matrix with ComplexF64 elements. + A = GBMatrix{ComplexF64}(1000, 1000) +1000x1000 GraphBLAS double complex matrix, hypersparse by row, ints: 32/32/32 + no entries, memory: 256 bytes + +julia> # Non-stored values are equal to the fill value of A, + # which is by default zero(eltype(A)) + A[1,5] == getfill(A) +true ``` Here we can already see several differences compared to `SparseArrays.SparseMatrixCSC`. @@ -78,19 +83,43 @@ As noted in the example above, `A` has a fill-value, which defaults to `zero(elt For normal linear algebra this is a good choice for fill-value, however GraphBLAS is intended for graphs. To handle this `SuiteSparseGraphBLAS.jl` support `missing` for the fill value (this is an active area of development, and a new fill value that acts like the identity for most operations is better suited to this task). -```@repl intro -A[1, 1] == zero(eltype(A)) +```jldoctest intro +julia> A[1, 1] == zero(eltype(A)) +true + +julia> B = setfill(A, missing) # no-copy alias +1000x1000 GraphBLAS double complex matrix, hypersparse by row, ints: 32/32/32 + no entries, memory: 256 bytes -B = setfill(A, missing) # no-copy alias -B[1, 1] +julia> B[1, 1] +missing ``` An empty matrix and vector won't do us much good, so let's see how to construct the matrix and vector from the graphic above. Both `A` and `v` below are constructed from coordinate format or COO. -```@repl intro -A = GBMatrix([1,1,2,2,3,4,4,5,6,7,7,7], [2,4,5,7,6,1,3,6,3,3,4,5], [1:12...]) - -v = GBVector([4], [10], 7) +```jldoctest intro +julia> A = GBMatrix([1,1,2,2,3,4,4,5,6,7,7,7], [2,4,5,7,6,1,3,6,3,3,4,5], [1:12...]) +7x7 GraphBLAS int64_t matrix, bitmap by row + 12 entries, memory: 681 bytes + + (1,2) 1 + (1,4) 2 + (2,5) 3 + (2,7) 4 + (3,6) 5 + (4,1) 6 + (4,3) 7 + (5,6) 8 + (6,3) 9 + (7,3) 10 + (7,4) 11 + (7,5) 12 + +julia> v = GBVector([4], [10], 7) +7x1 GraphBLAS int64_t matrix, bitmap by col + 1 entry, memory: 304 bytes + + (4,1) 10 ``` ## GraphBLAS Operations @@ -131,10 +160,40 @@ These built-in operators are *fast*, and should be used where possible. However, SuiteSparseGraphBLAS.jl will take care of operators behind the scenes, and in most cases users should pass in normal functions like `+` and `sin`. For example: -```@repl intro -emul(A, A, ^) # elementwise exponent - -map(sin, A) +```jldoctest intro +julia> emul(A, A, ^) # elementwise exponent +7x7 GraphBLAS int64_t matrix, bitmap by row + 12 entries, memory: 681 bytes + + (1,2) 1 + (1,4) 4 + (2,5) 27 + (2,7) 256 + (3,6) 3125 + (4,1) 46656 + (4,3) 823543 + (5,6) 16777216 + (6,3) 387420489 + (7,3) 10000000000 + (7,4) 285311670611 + (7,5) 8916100448256 + +julia> map(sin, A) +7x7 GraphBLAS double matrix, bitmap by row + 12 entries, memory: 681 bytes + + (1,2) 0.841471 + (1,4) 0.909297 + (2,5) 0.14112 + (2,7) -0.756802 + (3,6) -0.958924 + (4,1) -0.279415 + (4,3) 0.656987 + (5,6) 0.989358 + (6,3) 0.412118 + (7,3) -0.544021 + (7,4) -0.99999 + (7,5) -0.536573 ``` Broadcasting functionality is also supported, `A .^ A` will lower to `emul(A, A, ^)`, and `sin.(A)` will lower to `apply(sin, A)`. @@ -144,10 +203,27 @@ Matrix multiplication, which accepts a semiring, can be called with either `*(ma We can also use functions that are not already built into SuiteSparseGraphBLAS.jl: -```@repl intro -M = GBMatrix([[1,2] [3,4]]) -increment(x) = x + 1 -map(increment, M) +```jldoctest intro +julia> M = GBMatrix([[1,2] [3,4]]) +2x2 GraphBLAS int64_t matrix, full by col + 4 entries, memory: 272 bytes + + (1,1) 1 + (2,1) 2 + (1,2) 3 + (2,2) 4 + +julia> increment(x) = x + 1 +increment (generic function with 1 method) + +julia> map(increment, M) +2x2 GraphBLAS int64_t matrix, full by col + 4 entries, memory: 272 bytes + + (1,1) 2 + (2,1) 3 + (1,2) 4 + (2,2) 5 ``` Unfortunately this has a couple problems. The first is that it's slow.\ @@ -166,22 +242,52 @@ The methods are drawn from the LAGraph [repo](https://github.com/GraphBLAS/LAGra Input `A` must be a square, symmetric matrix with any element type. We'll test it using the matrix from the GBArray section above, which has two triangles in its undirected form. -```@repl intro -using SuiteSparseGraphBLAS: pair -function cohen(A) - U = select(triu, A) - L = select(tril, A) - return reduce(+, *(L, U, (+, pair); mask=A)) ÷ 2 -end - -function sandia(A) - L = select(tril, A) - return reduce(+, *(L, L, (+, pair); mask=L)) -end - -M = eadd(A, A', +) #Make undirected/symmetric -cohen(M) -sandia(M) +```jldoctest intro +julia> using SuiteSparseGraphBLAS: pair + +julia> function cohen(A) + U = select(triu, A) + L = select(tril, A) + return reduce(+, *(L, U, (+, pair); mask=A)) ÷ 2 + end +cohen (generic function with 1 method) + +julia> function sandia(A) + L = select(tril, A) + return reduce(+, *(L, L, (+, pair); mask=L)) + end +sandia (generic function with 1 method) + +julia> M = eadd(A, A', +) #Make undirected/symmetric +7x7 GraphBLAS int64_t matrix, bitmap by row + 20 entries, memory: 681 bytes + + (1,2) 1 + (1,4) 8 + (2,1) 1 + (2,5) 3 + (2,7) 4 + (3,4) 7 + (3,6) 14 + (3,7) 10 + (4,1) 8 + (4,3) 7 + (4,7) 11 + (5,2) 3 + (5,6) 8 + (5,7) 12 + (6,3) 14 + (6,5) 8 + (7,2) 4 + (7,3) 10 + (7,4) 11 + (7,5) 12 + +julia> cohen(M) +2 + +julia> sandia(M) +2 ``` # Citing diff --git a/docs/src/unaryops.md b/docs/src/unaryops.md index 9facc5fe..6361ecaa 100644 --- a/docs/src/unaryops.md +++ b/docs/src/unaryops.md @@ -1,27 +1,53 @@ # Unary Operators +```@meta +DocTestSetup = :(using SuiteSparseGraphBLAS) +DocTestFilters = [r"memory: \d+ bytes", r"Ptr{.*}\(0x[0-9a-f]+\)"] +``` + `UnaryOp`s are fairly straightforward $z = f(x)$ and their meaning should be clear from the name in most cases. `UnaryOp`s are used only in the `map` and [`apply`](@ref) functions. -```@repl -using SuiteSparseGraphBLAS +```jldoctest +julia> x = GBVector([1.5, 0, pi]) +3x1 GraphBLAS double matrix, full by col + 3 entries, memory: 264 bytes + + (1,1) 1.5 + (2,1) 0 + (3,1) 3.14159 + +julia> y = map(sin, x) +3x1 GraphBLAS double matrix, full by col + 3 entries, memory: 264 bytes -x = GBVector([1.5, 0, pi]) + (1,1) 0.997495 + (2,1) 0 + (3,1) 1.22465e-16 -y = map(sin, x) +julia> map(asin, y) +3x1 GraphBLAS double matrix, full by col + 3 entries, memory: 264 bytes -map(asin, y) + (1,1) 1.5 + (2,1) 0 + (3,1) 1.22465e-16 ``` Internally functions are lowered like this: -```@repl -using SuiteSparseGraphBLAS +```jldoctest +julia> op = unaryop(sin, Float64) +SuiteSparseGraphBLAS.TypedUnaryOperator{typeof(sin), Float64, Float64}(true, true, "GxB_SIN_FP64", Ptr{SuiteSparseGraphBLAS.LibGraphBLAS.GB_UnaryOp_opaque}(0x0000736051454500), sin, nothing) -op = unaryop(sin, Float64) +julia> map(op, GBVector([1.5, 0, pi])) +3x1 GraphBLAS double matrix, full by col + 3 entries, memory: 264 bytes -map(op, GBVector([1.5, 0, pi])) + (1,1) 0.997495 + (2,1) 0 + (3,1) 1.22465e-16 ``` ## Built-Ins From 16cd2516059544e7cfbcf261f793e8ca88800d05 Mon Sep 17 00:00:00 2001 From: "Viral B. Shah" Date: Tue, 27 Oct 2026 16:04:39 +0000 Subject: [PATCH 8/8] Add an Examples page to the docs List the algorithms in examples/ with their entry points and input conventions, and show a few of them as doctests on a small graph. Co-Authored-By: Claude Opus 5.5 --- docs/make.jl | 3 +- docs/src/examples.md | 71 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 docs/src/examples.md diff --git a/docs/make.jl b/docs/make.jl index 2417d00d..507d1db2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -19,7 +19,8 @@ makedocs( "selectops.md"#, #"udfs.md" ], - "Utilities" => "utilities.md" + "Utilities" => "utilities.md", + "Examples" => "examples.md" ] ) diff --git a/docs/src/examples.md b/docs/src/examples.md new file mode 100644 index 00000000..3b1798e1 --- /dev/null +++ b/docs/src/examples.md @@ -0,0 +1,71 @@ +# Examples + +```@meta +DocTestSetup = quote + using SuiteSparseGraphBLAS + for f in ("Cohen", "Sandia", "BFSParents", "BellmanFord") + include(joinpath(pkgdir(SuiteSparseGraphBLAS), "examples", "$f.jl")) + end +end +DocTestFilters = [r"memory: \d+ bytes"] +``` + +The [`examples`](https://github.com/JuliaSparse/SuiteSparseGraphBLAS.jl/tree/v11/examples) directory of the repository contains graph algorithms written with SuiteSparseGraphBLAS.jl. +They are not part of the package API: `include` a file to use the functions it defines. +Each file links to a paper or reference describing the algorithm, and all of them are tested in `test/examples.jl`. + +Unless noted otherwise `A` is the adjacency matrix of an undirected graph with `n` vertices. + +| Algorithm | File | Function | Notes | +|:----------|:-----|:---------|:------| +| Triangle counting (Cohen) | `Cohen.jl` | `cohen(A)` | | +| Triangle counting (Sandia) | `Sandia.jl` | `sandia(A)` | | +| Triangle counting (CMU) | `CMU.jl` | `cmu(A, n)` | | +| Triangles per vertex | `NodeWiseTriangleCount.jl` | `nodewise_tc(A)` | | +| k-truss | `KTruss.jl` | `ktruss(A, k)` | Returns the truss with edge supports as values. | +| BFS parents | `BFSParents.jl` | `bfs_parents(A, s, n)` | The parent of the source is `0`. | +| Multi-source BFS parents | `BFSParentsMultiSource.jl` | `ms_bfsp(A, S, n, s)` | Row `i` of the `s × n` matrix `S` marks source `i`. | +| Multi-source BFS levels | `BFSLevelsMultiSource.jl` | `ms_bfsl(A, S, n, s)` | `A` and `S` are `Bool`. Sources are level `1`, unreachable vertices `0`. | +| Bellman-Ford | `BellmanFord.jl` | `bellmanford(A, s, n)` | `A[i,j]` is the weight of edge `i → j`, with explicit zeros on the diagonal. | +| Floyd-Warshall | `FloydWarshall.jl` | `floydwarshall(A, n)` | Same input as Bellman-Ford. | +| Prim's minimum spanning tree | `BasicPrim.jl`, `AdvancedPrim.jl` | `b_prim(A, n, zeros(n))`, `a_prim(A, n, zeros(n))` | `A` is dense, with `Inf` where there is no edge and zeros on the diagonal. | +| Community detection by label propagation | `CDLP.jl` | `cdlp(A, n, t)` | Runs `t` iterations. Every vertex must have a neighbour. | + +The graph below has edges `1-2`, `1-3`, `2-3`, `2-4`, `3-4` and `4-5`, and two triangles, `{1,2,3}` and `{2,3,4}`. + +```jldoctest examples +julia> src = [1, 1, 2, 2, 3, 4]; dst = [2, 3, 3, 4, 4, 5]; + +julia> A = GBMatrix([src; dst], [dst; src], 1, 5, 5); + +julia> cohen(A), sandia(A) +(2, 2) + +julia> bfs_parents(A, 1, 5) +5x1 GraphBLAS int64_t matrix, bitmap by col + 5 entries, memory: 288 bytes + + (1,1) 0 + (2,1) 1 + (3,1) 1 + (4,1) 2 + (5,1) 4 +``` + +With edge weights, and the explicit zero diagonal that Bellman-Ford expects: + +```jldoctest examples +julia> w = [1.0, 4.0, 2.0, 7.0, 3.0, 1.0]; + +julia> W = GBMatrix([src; dst; 1:5], [dst; src; 1:5], [w; w; zeros(5)]); + +julia> bellmanford(W, 1, 5) +5x1 GraphBLAS double matrix, full by col + 5 entries, memory: 280 bytes + + (1,1) 0 + (2,1) 1 + (3,1) 3 + (4,1) 6 + (5,1) 7 +```