├── data ├── celegans.xy ├── bfs_example.labels ├── dfs_example.labels ├── bfs_example.xy ├── celegans.source ├── dfs_example.xy ├── biconnected_example.xy ├── all_shortest_paths_example.smat ├── dfs_example.smat ├── geoclusters-example.source ├── cores_example.xy ├── four_clusters.source ├── bfs_example.smat ├── four_clusters.xy ├── biconnected_example.smat ├── clr-24-1.smat ├── U3A.source ├── cores_example.smat ├── four_clusters.smat ├── clique-10.smat ├── Buck.xy ├── celegans.labels ├── Buck.smat ├── airports.labels ├── geoclusters-example.xy └── airports.xy ├── .gitignore ├── REQUIRE ├── .github └── workflows │ └── TagBot.yml ├── .travis.yml ├── test ├── triangles_test.jl ├── largest_component_test.jl ├── cosineknn_test.jl ├── dijkstra_test.jl ├── dirclustercoeffs_test.jl ├── floydwarshall_test.jl ├── sparse_to_csr_test.jl ├── csr_to_sparse_test.jl ├── corenums_test.jl ├── dfs_test.jl ├── bipartite_matching_test.jl ├── scomponents_test.jl ├── clustercoeffs_test.jl ├── runtests.jl ├── mst_prim_test.jl ├── matrixnetwork_test.jl ├── bfs_test.jl ├── utility_test.jl ├── biconnected_test.jl ├── generators_test.jl └── diffusions_test.jl ├── src ├── sparse_to_csr.jl ├── csr_to_sparse.jl ├── largest_component.jl ├── floydwarshall.jl ├── bfs.jl ├── corenums.jl ├── MatrixNetworks.jl ├── manage_data.jl ├── dfs.jl ├── cosineknn.jl ├── clustercoeffs.jl ├── dijkstra.jl ├── scomponents.jl ├── biconnected.jl ├── dirclustercoeffs.jl ├── MatrixNetwork.jl ├── mst_prim.jl ├── bipartite_matching.jl ├── triangles.jl └── spectral.jl ├── LICENSE.md ├── Project.toml ├── doc └── diffusions.md ├── appveyor.yml └── README.md /data/celegans.xy: -------------------------------------------------------------------------------- 1 | 0 0 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | TODO.txt -------------------------------------------------------------------------------- /data/bfs_example.labels: -------------------------------------------------------------------------------- 1 | rstuvwxy 2 | -------------------------------------------------------------------------------- /data/dfs_example.labels: -------------------------------------------------------------------------------- 1 | abcdefghi 2 | -------------------------------------------------------------------------------- /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 1.0 2 | DataStructures 3 | IterTools 4 | Arpack 5 | KahanSummation -------------------------------------------------------------------------------- /data/bfs_example.xy: -------------------------------------------------------------------------------- 1 | 0 1 2 | 1 1 3 | 2 1 4 | 3 1 5 | 0 0 6 | 1 0 7 | 2 0 8 | 3 0 9 | -------------------------------------------------------------------------------- /data/celegans.source: -------------------------------------------------------------------------------- 1 | Richard Durbin; Accessed via http://elegans.swmed.edu/parts/neurodata_readme.txt; Combined jsh and n2u sets -------------------------------------------------------------------------------- /data/dfs_example.xy: -------------------------------------------------------------------------------- 1 | 0 0 2 | 1 0 3 | 2 0 4 | 0 1 5 | 1 1 6 | 2 1 7 | 0 2 8 | 1 2 9 | 0 3 10 | -------------------------------------------------------------------------------- /data/biconnected_example.xy: -------------------------------------------------------------------------------- 1 | 0 0 2 | 1 0 3 | 2 0 4 | 0 1 5 | 1 1 6 | 2 1 7 | 0 2 8 | 1 2 9 | 0 3 10 | 0 4 11 | 4 0 12 | -------------------------------------------------------------------------------- /data/all_shortest_paths_example.smat: -------------------------------------------------------------------------------- 1 | 5 5 9 2 | 3 0 2 3 | 0 1 3 4 | 2 1 4 5 | 0 2 8 6 | 3 2 -5 7 | 1 3 1 8 | 4 3 6 9 | 0 4 -4 10 | 1 4 7 11 | -------------------------------------------------------------------------------- /data/dfs_example.smat: -------------------------------------------------------------------------------- 1 | 9 9 13 2 | 4 0 1 3 | 0 1 1 4 | 4 2 1 5 | 4 3 1 6 | 0 4 1 7 | 1 4 1 8 | 5 4 1 9 | 2 5 1 10 | 4 5 1 11 | 8 6 1 12 | 6 7 1 13 | 6 8 1 14 | 7 8 1 15 | -------------------------------------------------------------------------------- /.github/workflows/TagBot.yml: -------------------------------------------------------------------------------- 1 | name: TagBot 2 | on: 3 | schedule: 4 | - cron: 0 0 * * * 5 | jobs: 6 | TagBot: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: JuliaRegistries/TagBot@v1 10 | with: 11 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: julia 2 | os: 3 | - linux 4 | - osx 5 | julia: 6 | - 1.4 7 | - nightly 8 | notifications: 9 | email: false 10 | after_success: 11 | - julia -e 'cd(Pkg.dir("MatrixNetworks")); Pkg.add("Coverage"); using Coverage; Codecov.submit(process_folder())' 12 | -------------------------------------------------------------------------------- /data/geoclusters-example.source: -------------------------------------------------------------------------------- 1 | @InProceedings{Veldt-2016-simple-local-flow, 2 | Title = {A simple approach to localized flow-based methods}, 3 | Author = {Luke N. Veldt and David F. Gleich and Michael W. Mahoney}, 4 | Booktitle = {International Conference on Machine Learning}, 5 | Year = {2016}, 6 | } -------------------------------------------------------------------------------- /data/cores_example.xy: -------------------------------------------------------------------------------- 1 | 2 0 2 | 3 1 3 | 5 1 4 | 4 2 5 | 2 2 6 | 1 3 7 | 0 3 8 | 2 4 9 | 3.500000e+00 4.500000e+00 10 | 5 4 11 | 4 5 12 | 3 6 13 | 5 6 14 | 5 3 15 | 6 2 16 | 8.500000e+00 5.000000e-01 17 | 9 3 18 | 9 4 19 | 8 3 20 | 8 4 21 | 7 5 22 | -------------------------------------------------------------------------------- /data/four_clusters.source: -------------------------------------------------------------------------------- 1 | @Article{Gleich-2015-prbeyond, 2 | Title = {{PageRank} beyond the Web}, 3 | Author = {David F. Gleich}, 4 | Journal = {SIAM Review}, 5 | Pages = {321--363}, 6 | Volume = {57}, 7 | Year = {2015}, 8 | 9 | Month = {August}, 10 | Number = {3}, 11 | Doi = {10.1137/140976649}, 12 | } 13 | -------------------------------------------------------------------------------- /data/bfs_example.smat: -------------------------------------------------------------------------------- 1 | 8 8 18 2 | 1 0 1 3 | 4 0 1 4 | 0 1 1 5 | 5 1 1 6 | 3 2 1 7 | 5 2 1 8 | 6 2 1 9 | 2 3 1 10 | 7 3 1 11 | 0 4 1 12 | 1 5 1 13 | 2 5 1 14 | 6 5 1 15 | 2 6 1 16 | 5 6 1 17 | 7 6 1 18 | 3 7 1 19 | 6 7 1 20 | -------------------------------------------------------------------------------- /data/four_clusters.xy: -------------------------------------------------------------------------------- 1 | 112 72 2 | 105 107 3 | 145 116 4 | 154 79 5 | 182 114 6 | 119 146 7 | 158 153 8 | 197 79 9 | 202 149 10 | 305 107 11 | 303 139 12 | 362 123 13 | 349 62 14 | 342 156 15 | 300 71 16 | 124 235 17 | 165 238 18 | 204 235 19 | 183 272 20 | 153 203 21 | 130 274 22 | 220 276 23 | 311 258 24 | 306 217 25 | 336 194 26 | 352 240 27 | 275 195 28 | -------------------------------------------------------------------------------- /data/biconnected_example.smat: -------------------------------------------------------------------------------- 1 | 11 11 22 2 | 4 0 1 3 | 0 4 1 4 | 0 1 1 5 | 1 0 1 6 | 4 2 1 7 | 2 4 1 8 | 4 3 1 9 | 3 4 1 10 | 1 4 1 11 | 4 1 1 12 | 5 4 1 13 | 4 5 1 14 | 2 5 1 15 | 5 2 1 16 | 8 6 1 17 | 6 7 1 18 | 7 6 1 19 | 6 8 1 20 | 7 8 1 21 | 8 7 1 22 | 9 10 1 23 | 10 9 1 24 | -------------------------------------------------------------------------------- /test/triangles_test.jl: -------------------------------------------------------------------------------- 1 | @testset "triangles" begin 2 | A = load_matrix_network("clique-10") 3 | mytriangles = triangles(A) 4 | z = collect(mytriangles) 5 | @test length(z) == 120 6 | ei,ej,ek = unzip_triangles(z) 7 | @test length(ei) == 120 8 | 9 | A = erdos_renyi_undirected(100,0.2) 10 | z = collect(triangles(A;symmetries = true)) 11 | @test sum(Diagonal(sparse(A)^3)) == length(z) 12 | end 13 | -------------------------------------------------------------------------------- /test/largest_component_test.jl: -------------------------------------------------------------------------------- 1 | @testset "largest_component" begin 2 | A = load_matrix_network("dfs_example") 3 | (Acc,p) = largest_component(A) 4 | @test size(Acc,1) == 5 5 | 6 | (Acc,p) = largest_component(A,true) 7 | @test size(Acc,1) == 6 8 | 9 | A = load_matrix_network("cores_example") 10 | (Acc1,p1) = largest_component(A) 11 | (Acc2,p2) = largest_component(A,true) 12 | @test Acc1 == Acc2 13 | end 14 | -------------------------------------------------------------------------------- /test/cosineknn_test.jl: -------------------------------------------------------------------------------- 1 | @testset "cosineknn" begin 2 | A = load_matrix_network("bfs_example") 3 | S = cosineknn(A,2) 4 | 5 | # A = speye(Int64,4) 6 | A = sparse(1.0I,4,4) 7 | A[4,1] = 1 8 | CKNN = cosineknn(A,2) 9 | @test ((CKNN[4,1] *2)^2 - 2) <= 1e-7 10 | 11 | # A = speye(Int64,4) 12 | A = sparse(1.0I,4,4) 13 | A[4,1] = 1 14 | CKNN = cosineknn(MatrixNetwork(A),2) 15 | @test ((CKNN[4,1] *2)^2 - 2) <= 1e-7 16 | end 17 | -------------------------------------------------------------------------------- /data/clr-24-1.smat: -------------------------------------------------------------------------------- 1 | 9 9 28 2 | 1 0 4 3 | 7 0 8 4 | 0 1 4 5 | 2 1 8 6 | 7 1 11 7 | 1 2 8 8 | 3 2 7 9 | 5 2 4 10 | 8 2 2 11 | 2 3 7 12 | 4 3 9 13 | 5 3 14 14 | 3 4 9 15 | 5 4 10 16 | 2 5 4 17 | 3 5 14 18 | 4 5 10 19 | 6 5 2 20 | 5 6 2 21 | 7 6 1 22 | 8 6 6 23 | 0 7 8 24 | 1 7 11 25 | 6 7 1 26 | 8 7 7 27 | 2 8 2 28 | 6 8 6 29 | 7 8 7 30 | -------------------------------------------------------------------------------- /test/dijkstra_test.jl: -------------------------------------------------------------------------------- 1 | @testset "dijkstra" begin 2 | (A,xy,labels) = load_matrix_network_metadata("airports") 3 | A = -A; # fix funny encoding of airport data 4 | lax = 247; rst = 355 5 | 6 | (d,pred) = dijkstra(A,lax) 7 | @test maximum(d) == 540 8 | @test pred[end] == lax 9 | 10 | (d,pred) = dijkstra(MatrixNetwork(A),lax) 11 | @test maximum(d) == 540 12 | @test pred[end] == lax 13 | 14 | @test_throws ErrorException dijkstra(-sparse(1.0I,2,2), 1) 15 | end 16 | -------------------------------------------------------------------------------- /data/U3A.source: -------------------------------------------------------------------------------- 1 | @inproceedings{Lang-1993-finding, 2 | author = {Lang, Kevin and Rao, Satish}, 3 | title = {Finding Near-optimal Cuts: An Empirical Evaluation}, 4 | booktitle = {Proceedings of the Fourth Annual ACM-SIAM Symposium on Discrete Algorithms}, 5 | series = {SODA '93}, 6 | year = {1993}, 7 | isbn = {0-89871-313-7}, 8 | location = {Austin, Texas, USA}, 9 | pages = {212--221}, 10 | numpages = {10}, 11 | url = {http://dl.acm.org/citation.cfm?id=313559.313755}, 12 | acmid = {313755}, 13 | publisher = {Society for Industrial and Applied Mathematics}, 14 | address = {Philadelphia, PA, USA}, 15 | } 16 | -------------------------------------------------------------------------------- /test/dirclustercoeffs_test.jl: -------------------------------------------------------------------------------- 1 | @testset "dirclustercoeffs" begin 2 | (A,xy,labels) = load_matrix_network_metadata("celegans") 3 | (cc, cccyc, ccmid, ccin, ccout, nf) = dirclustercoeffs(A, true, true) 4 | @test length(cc) == 202 5 | (maxval, maxind) = findmax(cc) 6 | @test maxind == 113 7 | (cc, cccyc, ccmid, ccin, ccout, nf) = dirclustercoeffs(A) 8 | @test length(cc) == 202 9 | (maxval, maxind) = findmax(cc) 10 | @test maxind == 113 11 | (cc, cccyc, ccmid, ccin, ccout, nf) = dirclustercoeffs(A, true) 12 | @test length(cc) == 202 13 | (maxval, maxind) = findmax(cc) 14 | @test maxind == 113 15 | # negative weights 16 | @test_throws Exception dirclustercoeffs(-sparse(1.0I,5,5)) 17 | end 18 | -------------------------------------------------------------------------------- /test/floydwarshall_test.jl: -------------------------------------------------------------------------------- 1 | @testset "floydwarshall" begin 2 | A = load_matrix_network("all_shortest_paths_example") 3 | #A.nzval += abs(minimum(A)) + 1 # remove negative edges 4 | nzvals = nonzeros(A) 5 | val = abs(minimum(A)) + 1 6 | for ii=1:length(nzvals) 7 | nzvals[ii] += val 8 | end 9 | m = size(A,1) 10 | D2 = zeros(Float64,m,m) 11 | P2 = zeros(Float64,m,m) 12 | for ii=1:m 13 | (d,p) = dijkstra(A,ii) 14 | D2[ii,:] = d 15 | P2[ii,:] = p 16 | end 17 | 18 | (D,P) = floydwarshall(A) 19 | 20 | @test D == D2 21 | @test P == P2 22 | 23 | (D,P) = floydwarshall(MatrixNetwork(A)) 24 | 25 | @test D == D2 26 | @test P == P2 27 | end 28 | -------------------------------------------------------------------------------- /test/sparse_to_csr_test.jl: -------------------------------------------------------------------------------- 1 | @testset "sparse_to_csr" begin 2 | ei = [1;2;3] 3 | ej = [3;4;4] 4 | ev = [8;9;10] 5 | rp,ci,ai,m = sparse_to_csr(ei,ej,ev) 6 | 7 | # more tests added here 8 | for t = 1:100 9 | A = sprand(100,80,0.01) 10 | (rp,ci,ai,m)=sparse_to_csr(A) 11 | ii = zeros(Int64,length(ai)) 12 | j = zeros(Int64,length(ai)) 13 | a = zeros(Float64,length(ai)) 14 | n = length(rp)-1 15 | nz = 0 16 | for cr = 1:n 17 | for ri = rp[cr]:rp[cr+1]-1 18 | nz=nz+1 19 | ii[nz]=cr 20 | j[nz]=ci[ri] 21 | a[nz]=ai[ri] 22 | end 23 | end 24 | A2 = sparse(ii,j,a,n,m) 25 | @test A == A2 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /test/csr_to_sparse_test.jl: -------------------------------------------------------------------------------- 1 | @testset "csr_to_sparse" begin 2 | ei = [1;2;3] 3 | ej = [3;4;4] 4 | ev = [8;9;10] 5 | (rp,ci,ai,m) = sparse_to_csr(ei,ej,ev) 6 | (nzi,nzj,nzv) = csr_to_sparse(rp,ci,ai) 7 | A = sparse(nzi,nzj,nzv,length(rp)-1,maximum(ci)) 8 | 9 | # more tests added here 10 | # clique to sparse test 11 | rp = collect(1:5:26) 12 | #ci = vec(reshape(repmat(1:5,5,1)',25,1)) 13 | ci = copy(vec(repeat(1:5,5,1)')) 14 | ai = ones(Int64,25) 15 | A = csr_to_sparse_matrix(rp,ci,ai,5,5) 16 | @test Matrix(A) == ones(5,5) 17 | 18 | A = csr_to_sparse_matrix(rp,ci,ai) 19 | @test Matrix(A) == ones(5,5) 20 | 21 | # 100 random trials 22 | for t = 1:100 23 | A = sprand(100,80,0.01) 24 | (rp,ci,ai) = sparse_to_csr(A) 25 | A2 = csr_to_sparse_matrix(rp,ci,ai,100,80) 26 | @test A == A2 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /data/cores_example.smat: -------------------------------------------------------------------------------- 1 | 21 21 58 2 | 1 0 1 3 | 0 1 1 4 | 2 1 1 5 | 3 1 1 6 | 4 1 1 7 | 1 2 1 8 | 3 2 1 9 | 1 3 1 10 | 2 3 1 11 | 4 3 1 12 | 9 3 1 13 | 13 3 1 14 | 14 3 1 15 | 1 4 1 16 | 3 4 1 17 | 5 4 1 18 | 6 4 1 19 | 7 4 1 20 | 4 5 1 21 | 6 5 1 22 | 7 5 1 23 | 4 6 1 24 | 5 6 1 25 | 7 6 1 26 | 4 7 1 27 | 5 7 1 28 | 6 7 1 29 | 8 7 1 30 | 7 8 1 31 | 9 8 1 32 | 10 8 1 33 | 3 9 1 34 | 8 9 1 35 | 10 9 1 36 | 13 9 1 37 | 14 9 1 38 | 8 10 1 39 | 9 10 1 40 | 11 10 1 41 | 12 10 1 42 | 10 11 1 43 | 10 12 1 44 | 3 13 1 45 | 9 13 1 46 | 14 13 1 47 | 3 14 1 48 | 9 14 1 49 | 13 14 1 50 | 17 16 1 51 | 18 16 1 52 | 16 17 1 53 | 19 17 1 54 | 16 18 1 55 | 19 18 1 56 | 17 19 1 57 | 18 19 1 58 | 20 19 1 59 | 19 20 1 60 | -------------------------------------------------------------------------------- /src/sparse_to_csr.jl: -------------------------------------------------------------------------------- 1 | """ 2 | SPARSETOCSR 3 | ----------- 4 | convert sparse matrix into compressed row storage arrays 5 | and returns the row pointer (rp), column index (ci) and value index (ai) arrays 6 | of a compressed sparse representation of the matrix A (or its triplet format storage) 7 | 8 | Functions 9 | --------- 10 | - (rp,ci,ai,m) = sparse_to_csr{T}(A::SparseMatrixCSC{T,Int64}) 11 | - (rp,ci,ai,m) = sparse_to_csr{T}(nzi::Array{Int64,1},nzj::Array{Int64,1},nzv::Array{T,1}) 12 | 13 | Example 14 | ------- 15 | ~~~ 16 | i = [1;2;3] 17 | j = [3;4;4] 18 | v = [8;9;10] 19 | (rp,ci,ai,m) = sparse_to_csr(i,j,v) 20 | ~~~ 21 | """ 22 | function sparse_to_csr(A::SparseMatrixCSC{T,Int64}) where T 23 | At = copy(A'); 24 | return (At.colptr,At.rowval,At.nzval,At.m); 25 | end 26 | 27 | function sparse_to_csr(nzi::Array{Int64,1},nzj::Array{Int64,1}, 28 | nzv::Array{T,1}) where T 29 | At = sparse(nzj,nzi,nzv) 30 | return (At.colptr,At.rowval,At.nzval,At.m) 31 | end -------------------------------------------------------------------------------- /test/corenums_test.jl: -------------------------------------------------------------------------------- 1 | @testset "corenums" begin 2 | A = load_matrix_network("cores_example") 3 | A_ref = [1 0 1 1; 0 1 1 1; 1 1 0 1; 1 1 1 1] 4 | d_ref = [3;3;3;3] 5 | rt_ref = [1;2;3;4] 6 | @testset "MatrixNetwork" begin 7 | corenums(MatrixNetwork(A)) 8 | (d,rt) = corenums(MatrixNetwork(sparse(A_ref))) 9 | @test d_ref == d 10 | @test rt_ref == rt 11 | end 12 | @testset "SparseMatrixCSC" begin 13 | corenums(sparse(A)) 14 | (d,rt) = corenums(sparse(A_ref)) 15 | @test d_ref == d 16 | @test rt_ref == rt 17 | end 18 | @testset "CSR" begin 19 | corenums(sparse_to_csr(sparse(A))...) 20 | (d,rt) = corenums(sparse_to_csr(sparse(A_ref))...) 21 | @test d_ref == d 22 | @test rt_ref == rt 23 | end 24 | @testset "triplet" begin 25 | corenums(findnz(sparse(A_ref))[1], findnz(sparse(A_ref))[2]) 26 | (d,rt) = corenums(findnz(sparse(A_ref))[1], findnz(sparse(A_ref))[2]) 27 | @test d_ref == d 28 | @test rt_ref == rt 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/dfs_test.jl: -------------------------------------------------------------------------------- 1 | @testset "dfs" begin 2 | A = load_matrix_network("dfs_example") 3 | dfs(MatrixNetwork(A),1) 4 | 5 | n = 10 6 | O = ones(Int64,n-1) 7 | Z = zeros(Int64,n) 8 | A_ref = Tridiagonal(O,Z,O) 9 | B = sparse(Matrix(A_ref)) 10 | @testset "SparseMatrixCSC" begin 11 | dfs(A,1) 12 | 13 | (d,dt,ft,pred) = dfs(B,1) 14 | @test d == collect(0:n-1) 15 | @test dt == collect(0:n-1) 16 | @test ft == collect(2*n-1:-1:n) 17 | @test pred == collect(0:n-1) 18 | end 19 | @testset "CSR" begin 20 | dfs(sparse_to_csr(A)..., 1) 21 | 22 | (d,dt,ft,pred) = dfs(sparse_to_csr(B)..., 1) 23 | @test d == collect(0:n-1) 24 | @test dt == collect(0:n-1) 25 | @test ft == collect(2*n-1:-1:n) 26 | @test pred == collect(0:n-1) 27 | end 28 | @testset "triplet" begin 29 | dfs(findnz(A)[1], findnz(A)[2], 1) 30 | 31 | (d,dt,ft,pred) = dfs(findnz(B)[1], findnz(B)[2], 1) 32 | @test d == collect(0:n-1) 33 | @test dt == collect(0:n-1) 34 | @test ft == collect(2*n-1:-1:n) 35 | @test pred == collect(0:n-1) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MatrixNetworks.jl is licensed under the MIT "Expat" License: 2 | 3 | > Copyright (c) 2015 4 | > 5 | > Permission is hereby granted, free of charge, to any person obtaining 6 | > a copy of this software and associated documentation files (the 7 | > "Software"), to deal in the Software without restriction, including 8 | > without limitation the rights to use, copy, modify, merge, publish, 9 | > distribute, sublicense, and/or sell copies of the Software, and to 10 | > permit persons to whom the Software is furnished to do so, subject to 11 | > the following conditions: 12 | > 13 | > The above copyright notice and this permission notice shall be 14 | > included in all copies or substantial portions of the Software. 15 | > 16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | name = "MatrixNetworks" 2 | uuid = "4f449596-a032-5618-b826-5a251cb6dc11" 3 | authors = ["nassarhuda "] 4 | version = "1.0.1" 5 | 6 | [deps] 7 | Arpack = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97" 8 | DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" 9 | DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" 10 | IterTools = "c8e1da08-722c-5040-9ed9-7db0dc04731e" 11 | KahanSummation = "8e2b3108-d4c1-50be-a7a2-16352aec75c3" 12 | LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" 13 | Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" 14 | Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" 15 | SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" 16 | Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" 17 | 18 | [compat] 19 | Arpack = "0.4.0" 20 | DataStructures = "0.17.17" 21 | IterTools = "1.3.0" 22 | KahanSummation = "0.1.0" 23 | julia = "1" 24 | 25 | [extras] 26 | LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" 27 | Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" 28 | SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" 29 | Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" 30 | Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 31 | 32 | [targets] 33 | test = ["Statistics", "LinearAlgebra", "SparseArrays", "Random", "Test"] 34 | -------------------------------------------------------------------------------- /test/bipartite_matching_test.jl: -------------------------------------------------------------------------------- 1 | using LinearAlgebra 2 | 3 | @testset "bipartite_matching_test" begin 4 | W = sprand(10,8,0.5) 5 | bipartite_matching(W) 6 | bipartite_matching([10;12;13],[1;2;3],[3;2;4]) 7 | 8 | A = [5 1 1 1 1; 9 | 1 5 1 1 1; 10 | 1 1 5 1 1; 11 | 1 1 1 5 1; 12 | 1 1 1 1 5; 13 | 1 1 1 1 1] 14 | A = sparse(A) 15 | M1 = bipartite_matching(A) 16 | (m1,m2) = edge_list(M1) 17 | 18 | @test M1.weight == 25/maximum(abs.(A)) && isequal(m1,m2) 19 | 20 | A = ones(Int64,6,5) - Matrix(I,6,5) 21 | A = sparse(A') 22 | (ei,ej,ev) = csr_to_sparse(A.colptr,A.rowval,A.nzval) 23 | ai = [1;2;3;4;5;ei] 24 | aj = [1;2;3;4;5;ej] 25 | av = [5;5;5;5;5;ev] 26 | 27 | M2 = bipartite_matching(av,ai,aj) 28 | mi = MatrixNetworks.edge_indicator(M2,ai,aj) 29 | mitrue = zeros(Int64,length(av)) 30 | mitrue[1:5] .= 1 31 | 32 | @test M2.weight == 25/maximum(abs.(av)) && isequal(m1,m2) && sum(mi) ==5 && isequal(mi,mitrue) 33 | 34 | M2 = bipartite_matching(av,ai,aj,maximum(ai),maximum(aj)) 35 | mi = MatrixNetworks.edge_indicator(M2,ai,aj) 36 | mitrue = zeros(Int64,length(av)) 37 | mitrue[1:5] .= 1 38 | 39 | @test M2.weight == 25/maximum(abs.(av)) && isequal(m1,m2) && sum(mi) ==5 && isequal(mi,mitrue) 40 | end -------------------------------------------------------------------------------- /data/four_clusters.smat: -------------------------------------------------------------------------------- 1 | 27 27 102 2 | 1 0 1 3 | 2 0 1 4 | 4 0 1 5 | 5 0 1 6 | 0 1 1 7 | 3 1 1 8 | 0 2 1 9 | 5 2 1 10 | 6 2 1 11 | 1 3 1 12 | 4 3 1 13 | 6 3 1 14 | 7 3 1 15 | 0 4 1 16 | 3 4 1 17 | 5 4 1 18 | 7 4 1 19 | 8 4 1 20 | 9 4 1 21 | 0 5 1 22 | 2 5 1 23 | 4 5 1 24 | 6 5 1 25 | 2 6 1 26 | 3 6 1 27 | 5 6 1 28 | 8 6 1 29 | 3 7 1 30 | 4 7 1 31 | 8 7 1 32 | 4 8 1 33 | 6 8 1 34 | 7 8 1 35 | 19 8 1 36 | 4 9 1 37 | 10 9 1 38 | 11 9 1 39 | 13 9 1 40 | 14 9 1 41 | 9 10 1 42 | 11 10 1 43 | 12 10 1 44 | 13 10 1 45 | 9 11 1 46 | 10 11 1 47 | 12 11 1 48 | 13 11 1 49 | 14 11 1 50 | 10 12 1 51 | 11 12 1 52 | 14 12 1 53 | 9 13 1 54 | 10 13 1 55 | 11 13 1 56 | 24 13 1 57 | 9 14 1 58 | 11 14 1 59 | 12 14 1 60 | 16 15 1 61 | 18 15 1 62 | 19 15 1 63 | 20 15 1 64 | 21 15 1 65 | 15 16 1 66 | 17 16 1 67 | 18 16 1 68 | 19 16 1 69 | 16 17 1 70 | 19 17 1 71 | 20 17 1 72 | 21 17 1 73 | 15 18 1 74 | 16 18 1 75 | 8 19 1 76 | 15 19 1 77 | 16 19 1 78 | 17 19 1 79 | 15 20 1 80 | 17 20 1 81 | 15 21 1 82 | 17 21 1 83 | 22 21 1 84 | 21 22 1 85 | 23 22 1 86 | 24 22 1 87 | 25 22 1 88 | 26 22 1 89 | 22 23 1 90 | 24 23 1 91 | 25 23 1 92 | 26 23 1 93 | 13 24 1 94 | 22 24 1 95 | 23 24 1 96 | 25 24 1 97 | 26 24 1 98 | 22 25 1 99 | 23 25 1 100 | 24 25 1 101 | 22 26 1 102 | 23 26 1 103 | 24 26 1 104 | -------------------------------------------------------------------------------- /test/scomponents_test.jl: -------------------------------------------------------------------------------- 1 | @testset "scomponents" begin 2 | A = load_matrix_network("cores_example") 3 | sizes = [15;1;5] 4 | components = 3 5 | @testset "MatrixNetwork" begin 6 | cc = scomponents(MatrixNetwork(A)) 7 | @test cc.sizes == sizes 8 | @test length(cc.map) == 21 9 | @test cc.number == components 10 | end 11 | @testset "SparseMatrixCSC" begin 12 | cc = scomponents(A) 13 | @test cc.sizes == sizes 14 | @test length(cc.map) == 21 15 | @test cc.number == components 16 | end 17 | @testset "CSR" begin 18 | cc = scomponents(sparse_to_csr(A)...) 19 | @test cc.sizes == sizes 20 | @test length(cc.map) == 21 21 | @test cc.number == components 22 | end 23 | @testset "triplet" begin 24 | cc = scomponents(findnz(A)[1], findnz(A)[2]) 25 | @test cc.sizes == sizes 26 | @test length(cc.map) == 21 27 | @test cc.number == components 28 | end 29 | @testset "strong_components_map" begin 30 | sci = strong_components_map(MatrixNetwork(A)) 31 | @test strong_components_map(A) == sci 32 | @test strong_components_map(sparse_to_csr(A)...) == sci 33 | @test strong_components_map(findnz(A)[1], findnz(A)[2]) == sci 34 | end 35 | @testset "empty" begin 36 | @test scomponents(empty_graph(0)).sizes == [] 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /doc/diffusions.md: -------------------------------------------------------------------------------- 1 | Diffusions design document 2 | ========================== 3 | 4 | Should work for 5 | 6 | * PageRank 7 | * Heat Kernel 8 | * Katz 9 | * Single-source PageRank (ACL) 10 | * Single-source Heat kernel (hkrelax) 11 | * Single-source general (diffrelax) 12 | * And be as efficient and general as possible. 13 | 14 | Idea: 15 | 16 | High level-routines 17 | 18 | aclpagerank(A, alpha, Int, tol) 19 | aclpagerank(A, alpha, Set, tol) 20 | aclpagerank(A, alpha, Dict, tol) # weighted set 21 | aclpagerank(A, alpha, DegreeWeightedSet(Set), tol) 22 | # these all check the symmetry/undirected nature 23 | aclpagerank(SymmetryChecked(A), alpha, DegreeWeightedSet(Set), tol) 24 | # this skips that... 25 | 26 | pagerank(A, alpha) # tol is machine precision 27 | pagerank(A, alpha, tol) # is a variable 28 | 29 | personalized_pagerank(A,alpha,Set) # personalized on a set (uniformly) 30 | personalized_pagerank(A,alpha,Int) # personalized on a node 31 | personalized_pagerank(A,alpha,sparsevec) 32 | personalized_pagerank(A,alpha,Dict) 33 | personalized_pagerank(A,alpha,DegreeWeightedSet(Set)) 34 | 35 | Low level-routines 36 | 37 | pagerank_power! 38 | aclpagerank_push! 39 | 40 | Eventually, 41 | 42 | we'd like some type of intermediate interface: 43 | 44 | P = ACLPageRankProblem(A) # does symmetry checking 45 | P.solve(alpha, v, ...) 46 | 47 | PageRankDiffusion 48 | - This is a parametric type. 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /test/clustercoeffs_test.jl: -------------------------------------------------------------------------------- 1 | @testset "clustercoeffs" begin 2 | A = load_matrix_network("clique-10") 3 | v = ones(Int64,10) 4 | @testset "MatrixNetwork" begin 5 | cc = clustercoeffs(MatrixNetwork(A)) 6 | @test v == cc 7 | end 8 | @testset "SparseMatrixCSC" begin 9 | cc = clustercoeffs(A) 10 | @test v == cc 11 | end 12 | @testset "CSR" begin 13 | cc = clustercoeffs(sparse_to_csr(A)...) 14 | @test v == cc 15 | end 16 | @testset "triplet" begin 17 | cc = clustercoeffs(findnz(A)[1], findnz(A)[2]) 18 | @test v == cc 19 | end 20 | @testset "error throwing" begin 21 | # not undirected 22 | bad_mat = spdiagm(1=>[1;1]) #spdiagm(([1; 1],), 1, 3, 3) 23 | 24 | @test_throws ErrorException clustercoeffs(MatrixNetwork(bad_mat)) 25 | @test_throws ErrorException clustercoeffs(bad_mat) 26 | # negative weights 27 | @test_throws ErrorException clustercoeffs(MatrixNetwork(-sparse(1.0I,4,4))) 28 | @test_throws ErrorException clustercoeffs(-sparse(1.0I,4,4)) 29 | end 30 | cc_mn = clustercoeffs(MatrixNetwork(A), false, false) 31 | cc_csc = clustercoeffs(A, false, false) 32 | cc_csr = clustercoeffs(sparse_to_csr(A)..., false, false) 33 | cc_tri = clustercoeffs(findnz(A)[1], findnz(A)[2], false, false) 34 | @test cc_mn == cc_csc 35 | @test cc_csr == cc_csc 36 | @test cc_tri == cc_csc 37 | end 38 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using MatrixNetworks 2 | using Test 3 | using Random 4 | using Statistics 5 | using SparseArrays 6 | using Arpack 7 | using LinearAlgebra 8 | #using Lint 9 | 10 | # Todo 11 | # 1. Add core MatrixNetworks tests 12 | 13 | 14 | 15 | all_tests = [ 16 | "matrixnetwork", 17 | "utility", 18 | "generators", 19 | "bfs", 20 | "biconnected", 21 | "bipartite_matching", 22 | "clustercoeffs", 23 | "corenums", 24 | "cosineknn", 25 | "csr_to_sparse", 26 | "dfs", 27 | "diffusions", 28 | "dijkstra", 29 | "dirclustercoeffs", 30 | "floydwarshall", 31 | "largest_component", 32 | "mst_prim", 33 | "scomponents", 34 | "spectral", 35 | "sparse_to_csr", 36 | "triangles" 37 | ] 38 | 39 | #for t in all_tests 40 | for ti = 1:length(all_tests) 41 | t = all_tests[ti] 42 | test_name = join(["$(t)", "_test",".jl"]) 43 | @show test_name 44 | test_path = joinpath(dirname(@__FILE__), test_name) 45 | include(test_path) 46 | end 47 | 48 | 49 | 50 | #println("testing package with Lint...") 51 | #msgs = lintpkg( "MatrixNetworks", returnMsgs = true ) 52 | #if isempty(msgs) 53 | # info("Lint package passed") 54 | #else 55 | # warn("Lint package didn't pass") 56 | #end 57 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.4/julia-0.4-latest-win32.exe" 4 | - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.4/julia-0.4-latest-win64.exe" 5 | - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" 6 | - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" 7 | 8 | branches: 9 | only: 10 | - master 11 | - /release-.*/ 12 | 13 | notifications: 14 | - provider: Email 15 | on_build_success: false 16 | on_build_failure: false 17 | on_build_status_changed: false 18 | 19 | install: 20 | - ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" 21 | # Download most recent Julia Windows binary 22 | - ps: (new-object net.webclient).DownloadFile( 23 | $env:JULIA_URL, 24 | "C:\projects\julia-binary.exe") 25 | # Run installer silently, output to C:\projects\julia 26 | - C:\projects\julia-binary.exe /S /D=C:\projects\julia 27 | 28 | build_script: 29 | # Need to convert from shallow to complete for Pkg.clone to work 30 | - IF EXIST .git\shallow (git fetch --unshallow) 31 | - C:\projects\julia\bin\julia -e "versioninfo(); 32 | Pkg.clone(pwd(), \"MatrixNetworks\"); Pkg.build(\"MatrixNetworks\")" 33 | 34 | test_script: 35 | - C:\projects\julia\bin\julia --check-bounds=yes -e "Pkg.test(\"MatrixNetworks\")" 36 | -------------------------------------------------------------------------------- /data/clique-10.smat: -------------------------------------------------------------------------------- 1 | 10 10 90 2 | 1 0 1 3 | 2 0 1 4 | 3 0 1 5 | 4 0 1 6 | 5 0 1 7 | 6 0 1 8 | 7 0 1 9 | 8 0 1 10 | 9 0 1 11 | 0 1 1 12 | 2 1 1 13 | 3 1 1 14 | 4 1 1 15 | 5 1 1 16 | 6 1 1 17 | 7 1 1 18 | 8 1 1 19 | 9 1 1 20 | 0 2 1 21 | 1 2 1 22 | 3 2 1 23 | 4 2 1 24 | 5 2 1 25 | 6 2 1 26 | 7 2 1 27 | 8 2 1 28 | 9 2 1 29 | 0 3 1 30 | 1 3 1 31 | 2 3 1 32 | 4 3 1 33 | 5 3 1 34 | 6 3 1 35 | 7 3 1 36 | 8 3 1 37 | 9 3 1 38 | 0 4 1 39 | 1 4 1 40 | 2 4 1 41 | 3 4 1 42 | 5 4 1 43 | 6 4 1 44 | 7 4 1 45 | 8 4 1 46 | 9 4 1 47 | 0 5 1 48 | 1 5 1 49 | 2 5 1 50 | 3 5 1 51 | 4 5 1 52 | 6 5 1 53 | 7 5 1 54 | 8 5 1 55 | 9 5 1 56 | 0 6 1 57 | 1 6 1 58 | 2 6 1 59 | 3 6 1 60 | 4 6 1 61 | 5 6 1 62 | 7 6 1 63 | 8 6 1 64 | 9 6 1 65 | 0 7 1 66 | 1 7 1 67 | 2 7 1 68 | 3 7 1 69 | 4 7 1 70 | 5 7 1 71 | 6 7 1 72 | 8 7 1 73 | 9 7 1 74 | 0 8 1 75 | 1 8 1 76 | 2 8 1 77 | 3 8 1 78 | 4 8 1 79 | 5 8 1 80 | 6 8 1 81 | 7 8 1 82 | 9 8 1 83 | 0 9 1 84 | 1 9 1 85 | 2 9 1 86 | 3 9 1 87 | 4 9 1 88 | 5 9 1 89 | 6 9 1 90 | 7 9 1 91 | 8 9 1 92 | -------------------------------------------------------------------------------- /src/csr_to_sparse.jl: -------------------------------------------------------------------------------- 1 | """ 2 | CSRTOSPARSE 3 | ----------- 4 | convert a matrix from compressed sparse row to a sparse matrix A. 5 | It returns the arrays that feed the sparse function in julia. 6 | 7 | Functions 8 | --------- 9 | - (nzi,nzj,nzv) = csr_to_sparse{T}(rp::Vector{Int64},ci::Vector{Int64},ai::Vector{T}) 10 | - (nzi,nzj,nzv) = csr_to_sparse{T}(rp::Vector{Int64},ci::Vector{Int64},ai::Vector{T},nrows::Int64) 11 | 12 | Example 13 | ------- 14 | ~~~ 15 | i = [1;2;3] 16 | j = [3;4;4] 17 | v = [8;9;10] 18 | (rp,ci,ai,m) = sparse_to_csr(i,j,v) 19 | (nzi,nzj,nzv) = csr_to_sparse(rp,ci,ai) 20 | A = sparse(nzi,nzj,nzv,length(rp)-1,maximum(ci)) 21 | B = csr_to_sparse_matrix(rp,ci,ai) 22 | isequal(A,B) 23 | ~~~ 24 | """ 25 | function csr_to_sparse(rp::Vector{Int64},ci::Vector{Int64},ai::Vector{T}) where T 26 | nrows = length(rp)-1 27 | ncols = length(ci) 28 | nzi = zeros(Int64,ncols) 29 | for i=1:nrows 30 | for j=rp[i]:rp[i+1]-1 31 | nzi[j] = i 32 | end 33 | end 34 | nzj = ci 35 | nzv = ai 36 | return (nzi,nzj,nzv) 37 | end 38 | function csr_to_sparse_matrix(rp::Vector{Int64},ci::Vector{Int64},ai::Vector{T}) where T 39 | (i,j,k) = csr_to_sparse(rp,ci,ai) 40 | A = sparse(i,j,k) 41 | return A 42 | end 43 | function csr_to_sparse_matrix(rp::Vector{Int64},ci::Vector{Int64}, 44 | ai::Vector{T},nrows::Int64,ncols::Int64) where T 45 | (i,j,k) = csr_to_sparse(rp,ci,ai); 46 | A = sparse(i,j,k,nrows,ncols) 47 | return A 48 | end 49 | 50 | -------------------------------------------------------------------------------- /test/mst_prim_test.jl: -------------------------------------------------------------------------------- 1 | @testset "mst_prim" begin 2 | A = load_matrix_network("airports") 3 | A = -A 4 | T = mst_prim_matrix(A) 5 | 6 | A = load_matrix_network("clr-24-1") 7 | A[2,3] = 9 8 | A[3,2] = 9 9 | T = mst_prim_matrix(A) 10 | Ttrueijv = [ 11 | 2 8 1 4 6 9 3 5 4 3 7 6 8 1 7 3 12 | 1 1 2 3 3 3 4 4 5 6 6 7 7 8 8 9 13 | 4 8 4 7 4 2 7 9 9 4 2 2 1 8 1 2 ] 14 | Ttrue = sparse(vec(Ttrueijv[1,:]), vec(Ttrueijv[2,:]), vec(Ttrueijv[3,:]), 9,9) 15 | 16 | @test nnz(T - Ttrue) == 0 17 | 18 | A = load_matrix_network("clr-24-1") 19 | T1 = mst_prim_matrix(A) 20 | T2 = mst_prim_matrix(A,false,5) 21 | T3 = mst_prim_matrix(MatrixNetwork(A),false) 22 | T1T2diff = sparse([2,1],[3,8],[8,-8],9,9) 23 | T3T2diff = sparse([2,1],[3,8],[8,-8],9,9) 24 | 25 | @test nnz(triu(T1-T2) - T1T2diff) == 0 26 | @test nnz(triu(T3-T2) - T3T2diff) == 0 27 | 28 | @test_throws ErrorException mst_prim(MatrixNetwork(-sparse(1.0I,3,3))) 29 | @test_throws ErrorException mst_prim(MatrixNetwork(-sparse(1.0I,3,3)), false) 30 | @test_throws ErrorException mst_prim(MatrixNetwork(-sparse(1.0I,3,3)), false, 1) 31 | @test_throws ErrorException mst_prim(-sparse(1.0I,3,3)) 32 | @test_throws ErrorException mst_prim(-sparse(1.0I,3,3), false) 33 | @test_throws ErrorException mst_prim(-sparse(1.0I,3,3), false, 1) 34 | end 35 | -------------------------------------------------------------------------------- /src/largest_component.jl: -------------------------------------------------------------------------------- 1 | """ 2 | LARGEST_COMPONENT 3 | ----------------- 4 | return the largest connected component of A. 5 | Acc = largest_component(A) returns the largest connected component 6 | of the graph A. If A is directed, this returns the largest 7 | strongly connected component. 8 | 9 | Acc = largest_component(A,true) returns the largest connected piece of 10 | a directed graph where connectivity is undirected. Algorithmically, 11 | this takes A, drops the directions, then components the largest component 12 | and returns just this piece of the original _directed_ network. So the 13 | output Acc is directed in this case. 14 | 15 | (Acc,p) = largest_component(A) also returns a logical vector 16 | indicating which vertices in A were chosen. 17 | 18 | Functions 19 | --------- 20 | - (Acc,p) = largest_component{T}(A::SparseMatrixCSC{T,Int64}) 21 | - (Acc,p) = largest_component{T}(A::SparseMatrixCSC{T,Int64},sym::Bool) 22 | 23 | Example 24 | ------- 25 | ~~~ 26 | A = load_matrix_network("dfs_example") 27 | (Acc,p) = largest_component(A) 28 | ~~~ 29 | """ 30 | function largest_component(A::SparseMatrixCSC{T,Int64}) where T 31 | return largest_component(A,false) 32 | end 33 | 34 | function largest_component(A::SparseMatrixCSC{T,Int64},sym::Bool) where T 35 | if sym 36 | # As = A|A' until Julia implements this on sparse matrices, use this: 37 | As = max.(A,A') 38 | cc = scomponents(As) 39 | else 40 | cc = scomponents(A) 41 | end 42 | cind = argmax(cc.sizes) 43 | p = cc.map .== cind 44 | # no logical indexing so: 45 | idx = findall(p) 46 | Acc = A[idx,idx] 47 | return (Acc,p) 48 | end 49 | 50 | 51 | -------------------------------------------------------------------------------- /test/matrixnetwork_test.jl: -------------------------------------------------------------------------------- 1 | using SparseArrays 2 | using LinearAlgebra 3 | 4 | @testset "matrixnetwork" begin 5 | A = load_matrix_network("dfs_example") 6 | M = MatrixNetwork(A) 7 | B = sparse(M) 8 | @test A == B 9 | C = sparse_transpose(M) 10 | @test A' == C 11 | @test size(M) == size(B) 12 | @test_throws DomainError size(M, 0) 13 | @test size(M, 4) == 1 14 | @test ndims(M) == 2 15 | 16 | load_matrix_network_all("minnesota") 17 | load_matrix_network_all("U3A") 18 | 19 | #@show issym(sparse([0 1; 0 0]')) 20 | @test is_undirected(MatrixNetwork(sparse([0 1; 1 0]))) == true 21 | @test is_undirected(MatrixNetwork(sparse([0 1; 0 1]))) == false 22 | @test is_undirected(MatrixNetwork(load_matrix_network("dfs_example"))) == false 23 | @test is_undirected(sparse([0 1; 1 0])) == true 24 | 25 | @test is_connected(havel_hakimi_graph([1,1,1,1])) == false 26 | @test is_connected(havel_hakimi_graph([2,2,2])) == true 27 | @test is_connected(sparse([0 1 0; 0 0 1; 1 0 0])) == true 28 | @test is_connected(spzeros(1,1)) == true 29 | @test is_connected(empty_graph(0)) == false 30 | @test is_connected(empty_graph(1)) == true 31 | @test is_connected(empty_graph(2)) == false 32 | 33 | @test is_empty(empty_graph(0)) == true 34 | @test is_empty(MatrixNetwork(Int[],Int[],0)) == true 35 | @test is_empty(MatrixNetwork(Int[1],Int[1])) == false 36 | @test is_empty(erdos_renyi_undirected(0,0)) == true 37 | @test is_empty(erdos_renyi_undirected(1,0)) == false 38 | 39 | @testset "matvec" begin 40 | G = erdos_renyi_directed(5,2) 41 | y = collect(1:5) 42 | A = sparse(G) 43 | @test norm(A*y - G*y,2) <= 10*eps(1.0) 44 | @test norm(G'*y - A'*y,2) <= 10*eps(1.0) 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /data/Buck.xy: -------------------------------------------------------------------------------- 1 | 1.17482 -1.01359 2 | 1.37886 -2.55116 3 | 0.460687 -2.23053 4 | 1 -2 5 | 0.504409 -1.57469 6 | 0.970783 -1.22491 7 | 0.416964 -0.802263 8 | 1.37886 -0.802263 9 | 1.0728 -0.160999 10 | 0.431538 -0.000682905 11 | 1.10345 0.0631385 12 | 1.24483 0.220586 13 | 2.13518 0.547177 14 | 1.11653 0.831 15 | 1.88192 1.48566 16 | 2.89761 1.64688 17 | 1.54336 1.663 18 | 0.850108 1.74361 19 | 0.430934 1.84035 20 | 0.898475 2.13054 21 | 1.46275 2.14667 22 | 0.70501 2.30789 23 | 3.05883 2.37238 24 | 2.54293 2.66257 25 | 0.612157 2.43981 26 | 2.55399 2.93512 27 | 2 3 28 | 2.37951 3.149 29 | 1.90671 3.73436 30 | 2.06431 3.78502 31 | 2.22191 3.88633 32 | 3.34761 3.98202 33 | 3.67407 4.01016 34 | 3.91046 3.92573 35 | 0.932982 4.44356 36 | 3.57275 4.2353 37 | 2.71159 4.74187 38 | 2.92547 4.88258 39 | 3.08307 4.97827 40 | 3.07744 6.1884 41 | 4.00052 5.43417 42 | 0 1.9369 43 | 0 1.36725 44 | 0 0 45 | 0 -0.809347 46 | 0 -1.5 47 | 0 -2 48 | 0 -2.38072 49 | 0 -3.68879 50 | -1.17482 -1.01359 51 | -1.37886 -2.55116 52 | -0.460687 -2.23053 53 | -1 -2 54 | -0.504409 -1.57469 55 | -0.970783 -1.22491 56 | -0.416964 -0.802263 57 | -1.37886 -0.802263 58 | -1.0728 -0.160999 59 | -0.431538 -0.000682905 60 | -1.10345 0.0631385 61 | -1.24483 0.220586 62 | -2.13518 0.547177 63 | -1.11653 0.831 64 | -1.88192 1.48566 65 | -2.89761 1.64688 66 | -1.54336 1.663 67 | -0.850108 1.74361 68 | -0.430934 1.84035 69 | -0.898475 2.13054 70 | -1.46275 2.14667 71 | -0.70501 2.30789 72 | -3.05883 2.37238 73 | -2.54293 2.66257 74 | -0.612157 2.43981 75 | -2.55399 2.93512 76 | -2 3 77 | -2.37951 3.149 78 | -1.90671 3.73436 79 | -2.06431 3.78502 80 | -2.22191 3.88633 81 | -3.34761 3.98202 82 | -3.67407 4.01016 83 | -3.91046 3.92573 84 | -0.932982 4.44356 85 | -3.57275 4.2353 86 | -2.71159 4.74187 87 | -2.92547 4.88258 88 | -3.08307 4.97827 89 | -3.07744 6.1884 90 | -4.00052 5.43417 91 | -------------------------------------------------------------------------------- /test/bfs_test.jl: -------------------------------------------------------------------------------- 1 | @testset "bfs" begin 2 | A = load_matrix_network("bfs_example") 3 | A_ref = [1 0 1 1; 0 1 1 1; 1 1 0 1; 1 1 1 0] 4 | d_ref = [0,2,1,1] 5 | dt_ref = [0,3,1,2] 6 | pred_ref = [1,3,1,1] 7 | 8 | @testset "MatrixNetwork" begin 9 | bfs(MatrixNetwork(A), 1) 10 | 11 | (d,dt,pred) = bfs(MatrixNetwork(sparse(A_ref)), 1) 12 | @test d == d_ref 13 | @test dt_ref == dt 14 | @test pred_ref == pred 15 | 16 | bfs(MatrixNetwork(A), 1, 0) 17 | @test d == d_ref 18 | @test dt_ref == dt 19 | @test pred_ref == pred 20 | end 21 | @testset "SparseMatrixCSC" begin 22 | bfs(A,1) 23 | 24 | (d,dt,pred) = bfs(sparse(A_ref), 1) 25 | @test d == d_ref 26 | @test dt_ref == dt 27 | @test pred_ref == pred 28 | 29 | bfs(A,1,0) 30 | (d,dt,pred) = bfs(sparse(A_ref ),1,0) 31 | @test d == d_ref 32 | @test dt_ref == dt 33 | @test pred_ref == pred 34 | end 35 | @testset "triplet" begin 36 | bfs(findnz(A)[1], findnz(A)[2], 1) 37 | 38 | (d,dt,pred) = bfs(findnz(sparse(A_ref))[1], findnz(sparse(A_ref))[2],1) 39 | @test d == d_ref 40 | @test dt_ref == dt 41 | @test pred_ref == pred 42 | 43 | bfs(findnz(A)[1], findnz(A)[2], 1, 0) 44 | 45 | (d,dt,pred) = bfs(findnz(sparse(A_ref))[1], findnz(sparse(A_ref))[2], 1, 0) 46 | @test d == d_ref 47 | @test dt_ref == dt 48 | @test pred_ref == pred 49 | end 50 | @testset "CSR" begin 51 | A_csr = sparse_to_csr(A) 52 | bfs(A_csr..., 1) 53 | 54 | (d,dt,pred) = bfs(sparse_to_csr(sparse(A_ref))..., 1) 55 | @test d == d_ref 56 | @test dt_ref == dt 57 | @test pred_ref == pred 58 | 59 | bfs(A_csr..., 1, 0) 60 | 61 | (d,dt,pred) = bfs(sparse_to_csr(sparse(A_ref))..., 1, 0) 62 | @test d == d_ref 63 | @test dt_ref == dt 64 | @test pred_ref == pred 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /data/celegans.labels: -------------------------------------------------------------------------------- 1 | ADAL 2 | ADAR 3 | ADEL 4 | ADFL 5 | AIAL 6 | AIBL 7 | AIBR 8 | ASHL 9 | AVAL 10 | AVAR 11 | AVBL 12 | AVBR 13 | AVDL 14 | AVDR 15 | AVEL 16 | AVER 17 | AVJL 18 | AVJR 19 | AWAL 20 | FLPR 21 | PVPL 22 | PVQL 23 | PVR 24 | RICL 25 | RICR 26 | RIFL 27 | RIML 28 | RIPL 29 | RMGL 30 | SMDVR 31 | URBL 32 | URXL 33 | ADER 34 | ADFR 35 | AIAR 36 | ASHR 37 | FLPL 38 | PVQR 39 | RIMR 40 | RIPR 41 | RIVR 42 | RMGR 43 | SMDDR 44 | SMDVL 45 | URBR 46 | AINL 47 | ALA 48 | ALML 49 | AVFR 50 | AVKR 51 | AVL 52 | BDUL 53 | CEPDL 54 | IL1L 55 | IL2L 56 | OLLL 57 | PVPR 58 | RIAL 59 | RIGL 60 | RIGR 61 | RIH 62 | RIVL 63 | RMDL 64 | RMDR 65 | RMHL 66 | SIADR 67 | SIBDR 68 | SMBDR 69 | VA2 70 | ALMR 71 | ALNR 72 | AVKL 73 | AVM 74 | BDUR 75 | CEPDR 76 | IL2R 77 | OLLR 78 | RIFR 79 | RMDDL 80 | SAAVR 81 | SDQR 82 | SIAVR 83 | AIML 84 | AIYL 85 | AIZL 86 | AUAL 87 | AVHR 88 | AWBL 89 | OLQVL 90 | RIR 91 | SIBVL 92 | SMBDL 93 | SMBVL 94 | AIMR 95 | AIYR 96 | AIZR 97 | ASEL 98 | AUAR 99 | AVHL 100 | AWAR 101 | AWBR 102 | RIAR 103 | SMBVR 104 | URXR 105 | ADLL 106 | ADLR 107 | ASER 108 | ASKL 109 | AWCL 110 | CEPVL 111 | ASIR 112 | ASKR 113 | AWCR 114 | PVCL 115 | AFDL 116 | AFDR 117 | AINR 118 | BAGR 119 | BAGL 120 | ASGL 121 | ASIL 122 | ASJL 123 | AVFL 124 | HSNL 125 | ASGR 126 | DVB 127 | DVC 128 | HSNR 129 | RIBR 130 | RIS 131 | RMFL 132 | SAADL 133 | SAADR 134 | SAAVL 135 | SDQL 136 | DB1 137 | RIBL 138 | RMEV 139 | RMFR 140 | SMDDL 141 | VB1 142 | CEPshVL 143 | ASJR 144 | CEPshVR 145 | OLQDR 146 | PVNR 147 | SIBDL 148 | CEPshDR 149 | CEPVR 150 | RID 151 | DVA 152 | VB2 153 | PVCR 154 | PVNL 155 | RMDDR 156 | SIADL 157 | ALNL 158 | RMHR 159 | AQR 160 | IL2DL 161 | SABD 162 | URYDL 163 | URYVR 164 | SABVL 165 | URYDR 166 | URYVL 167 | SIBVR 168 | IL1R 169 | RMDVL 170 | RMDVR 171 | RMED 172 | RMEL 173 | IL2DR 174 | hyp 175 | VD1 176 | mu_bod 177 | DA1 178 | IL2VL 179 | URADL 180 | URADR 181 | IL1DL 182 | OLQDL 183 | IL1DR 184 | IL1VL 185 | SIAVL 186 | URAVL 187 | IL1VR 188 | IL2VR 189 | OLQVR 190 | URAVR 191 | VC2 192 | VC1 193 | GLRR 194 | GLRVL 195 | GLRVR 196 | RMER 197 | PLNL 198 | PLNR 199 | VC3 200 | GLRDL 201 | GLRDR 202 | GLRL 203 | -------------------------------------------------------------------------------- /src/floydwarshall.jl: -------------------------------------------------------------------------------- 1 | """ 2 | FLOYDWARSHALL 3 | ------------- 4 | compute all shortest paths using the Floyd-Warshall algorithm. 5 | 6 | (D,P) = floydwarshall(A) returns the shortest distance matrix between all pairs 7 | of nodes in the graph A in matrix D. If A has a negative weight cycle, then this 8 | algorithm will throw an error. P is the matrix of predecessors. 9 | 10 | Functions 11 | --------- 12 | - (D,P) = floydwarshall(A::MatrixNetwork) 13 | - (D,P) = floydwarshall{T}(A::SparseMatrixCSC{T,Int64}) 14 | 15 | Example 16 | ------- 17 | ~~~ 18 | A = load_matrix_network("all_shortest_paths_example") 19 | (D,P) = floydwarshall(A) 20 | ~~~ 21 | """ 22 | :floydwarshall 23 | 24 | ## setup functions: 25 | 26 | function floydwarshall_phase1(A::MatrixNetwork) 27 | (nzi,nzj,nzv) = csr_to_sparse(A.rp,A.ci,A.vals) 28 | return (nzi,nzj,nzv,A.n) 29 | end 30 | 31 | function floydwarshall_phase1(A::SparseMatrixCSC{T,Int64}) where T 32 | (ri,ci,ai) = findnz(A) 33 | return (ri,ci,ai,A.n) 34 | end 35 | 36 | function floydwarshall_phase2(ri::Vector{Int64},ci::Vector{Int64},ai::Vector{T},n::Int64) where T 37 | 38 | nz = length(ai) 39 | D = Inf*ones(Int64,n,n) 40 | 41 | #TODO: check: always compute P or give the option of just computing D? 42 | 43 | P = zeros(Int64,n,n) 44 | # initialize the distance and predecessor matrix 45 | for ei = 1:nz 46 | i = ri[ei] 47 | j = ci[ei] 48 | v = ai[ei] 49 | if v < D[i,j] 50 | D[i,j] = v 51 | P[i,j] = i 52 | end 53 | end 54 | 55 | ids = (LinearIndices((n,n)))[CartesianIndex.(1:n, 1:n)] 56 | D[ids] .= 0 # set diagonal to 0 57 | 58 | for k=1:n 59 | for i=1:n 60 | for j=1:n 61 | if D[i,k]+D[k,j] < D[i,j] 62 | D[i,j] = D[i,k]+D[k,j] 63 | P[i,j] = P[k,j] 64 | end 65 | end 66 | end 67 | end 68 | 69 | if any(diag(D).<0) 70 | warn("floydwarshall:negativeCycle","negative weight cycle detected") 71 | end 72 | 73 | return (D,P) 74 | end 75 | 76 | 77 | ## floyd warshall 78 | function floydwarshall(A::MatrixNetwork) 79 | (nzi,nzj,nzv,n) = floydwarshall_phase1(A) 80 | (D,P) = floydwarshall_phase2(nzi,nzj,nzv,n) 81 | return (D,P) 82 | end 83 | 84 | function floydwarshall(A::SparseMatrixCSC{T,Int64}) where T 85 | (nzi,nzj,nzv,n) = floydwarshall_phase1(A) 86 | (D,P) = floydwarshall_phase2(nzi,nzj,nzv,n) 87 | return (D,P) 88 | end 89 | -------------------------------------------------------------------------------- /test/utility_test.jl: -------------------------------------------------------------------------------- 1 | using SparseArrays 2 | using LinearAlgebra 3 | 4 | @testset "utility" begin 5 | @testset "random_edge" begin 6 | @test_throws ArgumentError random_edge(empty_graph(0)) 7 | @test_throws ArgumentError random_edge(empty_graph(5)) 8 | A = sparse([1],[2],1.0,5,5) 9 | @test random_edge(A)[1:2] == (1,2) 10 | @test random_edge(A)[1:2] == (1,2) 11 | @test random_edge(A)[1:2] == (1,2) 12 | @test random_edge(A)[1:2] == (1,2) 13 | G = lollipop_graph(5,3) 14 | A = sparse(G) 15 | # there are 16 edges, to 100*16*log(16) should randomly generate all of them 16 | M = zeros(8,8) 17 | Random.seed!(1) # make it determinstic-ish 18 | ntrials = 100*16*4 19 | for i=1:ntrials # log16 = 4 in base 2 20 | M[random_edge(G)[1:2]...] += 1 21 | end 22 | @test sum(M.*A) == ntrials # this means we got all 23 | # @test all(findall(!iszero,A.*(M .+ A)) .>= 1) # this means we got all entries 24 | @test all(x -> iszero(x) || x >= 1, A.*(M .+ A)) 25 | @test std(M[findall(iszero,M)]) <= 100 26 | 27 | n = 16 28 | A = sparse(1:n-1,2:n,1,n,n) 29 | # there are 15 edges, to 100*15*log(15) should randomly generate all of them 30 | M = zeros(n,n) 31 | Random.seed!(1) # make it determinstic-ish 32 | ntrials = 100*16*4 # we just use the same one 33 | for i=1:ntrials # log16 = 4 in base 2 34 | M[random_edge(A)[1:2]...] += 1 35 | end 36 | @test sum(M.*A) == ntrials # this means we got all 37 | # @test all(findall(iszero,A.*(M .+ A)) .>= 1) # this means we got all entries 38 | @test all(x -> iszero(x) || x >= 1, A.*(M .+ A)) 39 | @test std(M[findall(iszero,M)]) <= 100 40 | end 41 | @testset "undirected_edges" begin 42 | @test map(length, undirected_edges(empty_graph(0))) == (0,0) 43 | @test map(length, undirected_edges(empty_graph(1))) == (0,0) 44 | @test map(length, undirected_edges(lollipop_graph(5,1))) == (5,5) 45 | @test undirected_edges(lollipop_graph(5,1)) == ([1,2,3,4,5],[2,3,4,5,6]) 46 | A = sparse([2],[1],1.0,5,5) 47 | @test map(length, undirected_edges(MatrixNetwork(A))) == (0,0) 48 | end 49 | @testset "directed_edges" begin 50 | @test map(length, directed_edges(empty_graph(0))) == (0,0) 51 | @test map(length, directed_edges(empty_graph(1))) == (0,0) 52 | @test map(length, directed_edges(lollipop_graph(5,1))) == (10,10) 53 | A = sparse([1],[2],1.0,5,5) 54 | @test map(length, directed_edges(MatrixNetwork(A))) == (1,1) 55 | @test directed_edges(MatrixNetwork(A)) == ([1],[2]) 56 | end 57 | end -------------------------------------------------------------------------------- /src/bfs.jl: -------------------------------------------------------------------------------- 1 | """ 2 | BFS 3 | --- 4 | compute breadth first search distances and returns a distance(d), 5 | the discover time(dt), predecessor array(pred) in the tuple (d,dt,pred) 6 | pred[i] = 0 if vertex i is in a component not reachable from u and i != u. 7 | Search stops when it hits the vertex target. 8 | 9 | Functions 10 | --------- 11 | * (d,dt,pred) = bfs(A::MatrixNetwork,u::Int64,target::Int64) 12 | * (d,dt,pred) = bfs{T}(A::SparseMatrixCSC{T,Int64}),u::Int64,target::Int64) 13 | * (d,dt,pred) = bfs(ei::Vector{Int64},ej::Vector{Int64},u::Int64,target::Int64)\n 14 | If target is not specified, it is assigned to 0 15 | 16 | Example 17 | ------- 18 | ~~~ 19 | A = load_matrix_network("bfs_example") 20 | (d,dt,pred) = bfs(A,1) 21 | ~~~ 22 | """ 23 | function bfs(A::MatrixNetwork,u::Int64,target::Int64) 24 | (rp,ci) = (A.rp,A.ci) 25 | n=length(rp)-1 26 | d=-1*ones(Int64,n) 27 | dt=-1*ones(Int64,n) 28 | pred=zeros(Int64,n) 29 | sq=zeros(Int64,n) 30 | sqt=0 31 | sqh=0 # search queue and search queue tail/head 32 | 33 | # start bfs at u 34 | sqt=sqt+1 35 | sq[sqt]=u 36 | t=0 37 | d[u]=0 38 | dt[u]=t 39 | t=t+1 40 | pred[u]=u 41 | while sqt-sqh>0 42 | sqh=sqh+1 43 | v=sq[sqh] # pop v off the head of the queue 44 | for ri=rp[v]:rp[v+1]-1 45 | w=ci[ri] 46 | if d[w]<0 47 | sqt=sqt+1 48 | sq[sqt]=w 49 | d[w]=d[v]+1 50 | dt[w]=t 51 | t=t+1 52 | pred[w]=v 53 | if w==target 54 | return (d,dt,pred) 55 | end 56 | end 57 | end 58 | end 59 | return (d,dt,pred) 60 | end 61 | 62 | ############################ 63 | ### Additional functions ### 64 | ############################ 65 | 66 | function bfs(A::MatrixNetwork,u::Int64) 67 | return bfs(A,u,0) 68 | end 69 | 70 | ## CSC sparse matrices: 71 | function bfs(A::SparseMatrixCSC{T,Int64},u::Int64) where T 72 | return bfs(MatrixNetwork(A),u) 73 | end 74 | 75 | function bfs(A::SparseMatrixCSC{T,Int64},u::Int64,target::Int64) where T 76 | return bfs(MatrixNetwork(A),u,target) 77 | end 78 | 79 | ## Triplet Format: 80 | function bfs(ei::Vector{Int64},ej::Vector{Int64},u::Int64) 81 | return bfs(MatrixNetwork(ei,ej),u) 82 | end 83 | 84 | function bfs(ei::Vector{Int64},ej::Vector{Int64},u::Int64,target::Int64) 85 | return bfs(MatrixNetwork(ei,ej),u,target) 86 | end 87 | 88 | ## CSR sparse matrices: 89 | function bfs(rp::Vector{Int64},ci::Vector{Int64},vals::Vector{T},n::Int64,u::Int64) where T 90 | return bfs(MatrixNetwork(n,rp,ci,vals),u) 91 | end 92 | 93 | function bfs(rp::Vector{Int64},ci::Vector{Int64},vals::Vector{T},n::Int64,u::Int64,target::Int64) where T 94 | return bfs(MatrixNetwork(n,rp,ci,vals),u,target) 95 | end -------------------------------------------------------------------------------- /src/corenums.jl: -------------------------------------------------------------------------------- 1 | """ 2 | CORENUMS 3 | --------- 4 | compute the core number for each vertex in the graph and returns the core 5 | numbers for each vertex of the graph A along with the removal order of the vertex in the 6 | tuple (d,rt). This function works on directed graphs but gives the in-degree core number. 7 | To get the out-degree core numbers call corenums(A') 8 | 9 | Functions 10 | --------- 11 | - (d,rt) = corenums(A::MatrixNetwork) 12 | - (d,rt) = corenums{T}(A::SparseMatrixCSC{T,Int64}) 13 | - (d,rt) = corenums(ei::Vector{Int64},ej::Vector{Int64}) 14 | 15 | Example 16 | ------- 17 | ~~~ 18 | A = load_matrix_network("cores_example") 19 | (d,rt) = corenums(A) 20 | ~~~ 21 | """ 22 | function corenums(A::MatrixNetwork) 23 | (rp,ci) = (A.rp,A.ci) 24 | n=length(rp)-1 25 | nz=length(ci) 26 | 27 | d=zeros(Int64,n) 28 | maxd=0 29 | rt=zeros(Int64,n) 30 | for k=1:nz 31 | newd=d[ci[k]]+1 32 | d[ci[k]]=newd 33 | if newd>maxd 34 | maxd=newd 35 | end 36 | end 37 | 38 | # compute the bucket sort 39 | dp=zeros(Int64,maxd+2) 40 | vs=zeros(Int64,n) 41 | vi=zeros(Int64,n)# degree position, vertices 42 | for i=1:n 43 | dp[d[i]+2]=dp[d[i]+2]+1 44 | end # plus 2 because degrees start at 0 45 | 46 | dp=cumsum(dp) 47 | dp=dp.+1 48 | for i=1:n 49 | vs[dp[d[i]+1]]=i 50 | vi[i]=dp[d[i]+1] 51 | dp[d[i]+1]=dp[d[i]+1]+1 52 | end 53 | 54 | for i=maxd:-1:1 55 | dp[i+1]=dp[i] 56 | end 57 | 58 | # start the algorithm 59 | t=1 60 | for i=1:n 61 | v = vs[i] 62 | dv = d[v] 63 | rt[v]=t 64 | t=t+1 65 | 66 | for rpi=rp[v]:rp[v+1]-1 67 | w=ci[rpi] 68 | dw=d[w] 69 | 70 | if dw<=dv # we already removed w 71 | else #need to remove edge (v,w), which decreases d(w) 72 | # swap w with the vertex at the head of its degree 73 | pw=vi[w] # get the position of w 74 | px=dp[dw+1] #get the pos of the vertex at the head of dw list 75 | x=vs[px] 76 | #swap w, x 77 | vs[pw]=x 78 | vs[px]=w 79 | vi[w]=px 80 | vi[x]=pw 81 | 82 | # decrement the degree of w and increment the start of dw 83 | d[w]=dw-1 84 | dp[dw+1]=px+1 85 | end 86 | end 87 | end 88 | return (d,rt) 89 | end 90 | 91 | ############################ 92 | ### Additional functions ### 93 | ############################ 94 | 95 | ## CSC: 96 | function corenums(A::SparseMatrixCSC{T,Int64}) where T 97 | return corenums(MatrixNetwork(A)) 98 | end 99 | 100 | ## Triplet Format: 101 | function corenums(ei::Vector{Int64},ej::Vector{Int64}) 102 | return corenums(MatrixNetwork(ei,ej)) 103 | end 104 | 105 | ## CSR sparse matrices: 106 | function corenums(rp::Vector{Int64},ci::Vector{Int64},vals::Vector{T},n::Int64) where T 107 | return corenums(MatrixNetwork(n,rp,ci,vals)) 108 | end -------------------------------------------------------------------------------- /src/MatrixNetworks.jl: -------------------------------------------------------------------------------- 1 | module MatrixNetworks 2 | 3 | using DataStructures 4 | using SparseArrays 5 | using LinearAlgebra 6 | using Printf 7 | using Arpack 8 | using DelimitedFiles 9 | using Random 10 | using Statistics 11 | using KahanSummation 12 | 13 | """ 14 | Module ``MatrixNetworks``: Documentation on the module 15 | 16 | - Option 1: start with a sparse matrix A: 17 | - example: ``M = MatrixNetwork(A)`` 18 | - Option 2: start with row and column indexes for the nonzeros in the matrix 19 | - example: ``M = MatrixNetwork(ei,ej)`` 20 | 21 | Some available functions: (use ?function_name to get more documentation) 22 | - bfs 23 | - bipartite_matching 24 | - clustercoeffs 25 | - corenums 26 | - cosineknn 27 | - csr_to_sparse 28 | - dfs 29 | - biconnected_components 30 | - dijkstra 31 | - dirclustercoeffs 32 | - floydwarshall 33 | - largest_component 34 | - mst_prim 35 | - scomponents 36 | - sparse_to_csr 37 | - pagerank 38 | - erdos_renyi_undirected 39 | - biconnected_components 40 | - triangles 41 | 42 | You can check the readme file here: \n 43 | "https://github.com/nassarhuda/MatrixNetworks.jl/blob/master/README.md" 44 | """ 45 | MatrixNetworks 46 | 47 | include("MatrixNetwork.jl") 48 | export MatrixNetwork, sparse_transpose, 49 | is_undirected, is_connected, is_empty, empty_graph, 50 | random_edge, undirected_edges, directed_edges 51 | 52 | include("scomponents.jl") 53 | include("csr_to_sparse.jl") 54 | include("sparse_to_csr.jl") 55 | include("bipartite_matching.jl") 56 | include("bfs.jl") 57 | include("dfs.jl") 58 | include("clustercoeffs.jl") 59 | include("corenums.jl") 60 | include("floydwarshall.jl") 61 | 62 | include("manage_data.jl") 63 | export load_matrix_network, load_matrix_network_metadata, load_matrix_network_all, 64 | matrix_network_datasets 65 | include("largest_component.jl") 66 | include("cosineknn.jl") 67 | include("dirclustercoeffs.jl") 68 | include("dijkstra.jl") 69 | include("mst_prim.jl") 70 | 71 | include("spectral.jl") 72 | export fiedler_vector, sweepcut, spectral_cut, bestset, SweepcutProfile 73 | 74 | # export everything to make them accessible as functions 75 | export bipartite_matching, edge_list, create_sparse, 76 | bipartite_matching_setup, bipartite_matching_indicator, bfs, 77 | dfs, clustercoeffs, corenums, scomponents, strong_components_map, 78 | enrich, csr_to_sparse, floydwarshall, largest_component, 79 | sparse_to_csr, cosineknn, dirclustercoeffs, dijkstra, mst_prim, mst_prim_matrix, 80 | csr_to_sparse_matrix, edge_indicator 81 | 82 | include("diffusions.jl") 83 | export pagerank, pagerank_power!, personalized_pagerank, seeded_pagerank, stochastic_mult!, 84 | seeded_stochastic_heat_kernel, stochastic_heat_kernel_series! 85 | 86 | include("generators.jl") 87 | export erdos_renyi_undirected, erdos_renyi_directed, 88 | erdős_rényi_undirected, erdős_rényi_directed, 89 | chung_lu_undirected, is_graphical_sequence, havel_hakimi_graph, 90 | pa_graph, preferential_attachment_graph, 91 | pa_edges!, preferential_attachment_edges!, 92 | gpa_graph, generalized_preferential_attachment_graph, 93 | gpa_edges!, generalized_preferential_attachment_edges!, 94 | roach_graph, lollipop_graph 95 | 96 | include("biconnected.jl") 97 | export biconnected_components, biconnected_components! 98 | 99 | include("triangles.jl") 100 | export unzip_triangles,triangles 101 | 102 | end # end module 103 | -------------------------------------------------------------------------------- /src/manage_data.jl: -------------------------------------------------------------------------------- 1 | using DelimitedFiles 2 | 3 | function readSMAT(filename::AbstractString) 4 | f = open(filename) 5 | header = readline(f) 6 | headerparts = split(header) 7 | nedges = parse(Int,headerparts[3]) 8 | ei = zeros(Int64,nedges) 9 | ej = zeros(Int64, nedges) 10 | ev = zeros(Float64, nedges) 11 | @inbounds for i = 1:nedges 12 | curline = readline(f) 13 | parts = split(curline) 14 | ei[i] = parse(Int, parts[1])+1 15 | ej[i] = parse(Int, parts[2])+1 16 | ev[i] = parse(Float64, parts[3]) 17 | end 18 | close(f) 19 | A = sparse(ei, ej, ev, 20 | parse(Int,headerparts[1]), 21 | parse(Int,headerparts[2]) 22 | ) 23 | return A 24 | end 25 | 26 | 27 | 28 | mutable struct MatrixNetworkMetadata 29 | A::SparseMatrixCSC{Int64,Int64} 30 | labels::Vector{AbstractString} 31 | xy::Array{Float64,2} 32 | source::AbstractString 33 | end 34 | 35 | function load_matrix_network_all(name::AbstractString) 36 | A = load_matrix_network(name) 37 | pathname = joinpath(dirname(dirname(@__FILE__)),"data") 38 | 39 | meta_source = joinpath(pathname,"$(name).source") 40 | if isfile(meta_source) 41 | # source = open(readstring, meta_source) 42 | source = open(s->read(s,String),meta_source) 43 | else 44 | source = "(None given)" 45 | end 46 | 47 | meta_xy = joinpath(pathname,"$(name).xy") 48 | if isfile(meta_xy) 49 | xy = readdlm(meta_xy) 50 | else 51 | xy = zeros(0,2) 52 | end 53 | 54 | meta_labels = joinpath(pathname,"$(name).labels") 55 | if isfile(meta_labels) 56 | labels = open(readlines, meta_labels) 57 | else 58 | labels = map(x -> @sprintf("%i",x), 1:size(A,1)) 59 | end 60 | 61 | return MatrixNetworkMetadata(A,labels,xy,source) 62 | end 63 | 64 | 65 | function load_matrix_network(name::AbstractString) 66 | pathname = joinpath(dirname(dirname(@__FILE__)),"data") 67 | smatfile = joinpath(pathname,"$(name).smat") 68 | if isfile(smatfile) 69 | return readSMAT(smatfile) 70 | else 71 | error(@sprintf "The example datafile '%s' does not seem to exist where it should\n" name) 72 | end 73 | end 74 | 75 | function load_matrix_network_metadata(name::AbstractString) 76 | pathname = joinpath(dirname(dirname(@__FILE__)),"data") 77 | smatfile = joinpath(pathname,"$(name).smat") 78 | meta_xy = joinpath(pathname,"$(name).xy") 79 | meta_labels = joinpath(pathname,"$(name).labels") 80 | if isfile(smatfile) 81 | if isfile(meta_xy) && isfile(meta_labels) 82 | xy = readdlm(meta_xy) 83 | labels = readdlm(meta_labels) 84 | return (readSMAT(smatfile),xy,labels) 85 | end 86 | else 87 | error(@sprintf "The example datafile '%s' does not seem to exist where it should\n" name) 88 | end 89 | end 90 | 91 | function matrix_network_datasets() 92 | datasets_location = joinpath(dirname(dirname(@__FILE__)),"data") 93 | content = readdir(datasets_location) 94 | cc = map(i->match(r".smat",content[i]),1:length(content)) 95 | smat_files = content[findall(typeof.(cc).!=Nothing)] 96 | for i = 1:length(smat_files) 97 | smat_files[i] = smat_files[i][1:end-5] 98 | end 99 | return smat_files 100 | end 101 | 102 | -------------------------------------------------------------------------------- /test/biconnected_test.jl: -------------------------------------------------------------------------------- 1 | using LinearAlgebra 2 | @testset "biconnected" begin 3 | n = 10 4 | O = ones(Int64,n-1) 5 | Z = zeros(Int64,n) 6 | A = Tridiagonal(O,Z,O) 7 | B = MatrixNetwork(dropzeros!(sparse(A))) 8 | obj = biconnected_components(B) 9 | 10 | for i = 2:n-1 11 | @test obj.articulation_points[i]!=0 12 | end 13 | components = biconnected_components(B;art=false, components=false).number 14 | number_of_components = biconnected_components!(B, zeros(Bool,0), zeros(Int64,0)) 15 | @test 2*components == 2*number_of_components == length(obj.map) 16 | 17 | @test obj.articulation_points[1]==0 18 | @test obj.articulation_points[n]==0 19 | 20 | B = dropzeros!(sparse(A)) 21 | obj = biconnected_components(B) 22 | 23 | for i = 2:n-1 24 | @test obj.articulation_points[i]!=0 25 | end 26 | components = biconnected_components(B;art=false, components=false).number 27 | 28 | @test obj.articulation_points[1]==0 29 | @test obj.articulation_points[n]==0 30 | 31 | B = sparse_to_csr(dropzeros!(sparse(A))) 32 | obj = biconnected_components(B...) 33 | for i = 2:n-1 34 | @test obj.articulation_points[i]!=0 35 | end 36 | components = biconnected_components(B...;art=false, components=false).number 37 | 38 | @test obj.articulation_points[1]==0 39 | @test obj.articulation_points[n]==0 40 | 41 | B = findnz(dropzeros!(sparse(A))) 42 | obj = biconnected_components(B[1], B[2]) 43 | 44 | for i = 2:n-1 45 | @test obj.articulation_points[i]!=0 46 | end 47 | components = biconnected_components(B[1], B[2];art=false, components=false).number 48 | 49 | @test obj.articulation_points[1]==0 50 | @test obj.articulation_points[n]==0 51 | 52 | A = load_matrix_network("minnesota") 53 | B = MatrixNetwork(A) 54 | components = biconnected_components(B;art=false, components=false).number 55 | number_of_components = biconnected_components!(B, zeros(Bool,0), zeros(Int64,0)) 56 | @test components == number_of_components == 142 57 | 58 | A = load_matrix_network("biconnected_example") 59 | B = MatrixNetwork(A) 60 | obj = biconnected_components(B) 61 | components = biconnected_components(B;art=false, components=false).number 62 | number_of_components = biconnected_components!(B, zeros(Bool,0), zeros(Int64,0)) 63 | @test components == number_of_components == 5 64 | @test obj.articulation_points[5] == 1 65 | 66 | A = load_matrix_network("cores_example") 67 | B = MatrixNetwork(A) 68 | obj = biconnected_components(B) 69 | components = biconnected_components(B;art=false, components=false).number 70 | number_of_components = biconnected_components!(B, zeros(Bool,0), zeros(Int64,0)) 71 | @test components == number_of_components == 6 72 | @test obj.articulation_points[20] == 1 73 | @test obj.articulation_points[2] == 1 74 | @test obj.articulation_points[11] == 1 75 | @test obj.articulation_points[12] == 0 76 | 77 | A = empty_graph() 78 | obj = biconnected_components(A) 79 | components = biconnected_components(A;art=false, components=false).number 80 | number_of_components = biconnected_components!(A, zeros(Bool,0), zeros(Int64,0)) 81 | @test components == number_of_components == 0 82 | 83 | A = load_matrix_network("clique-10") 84 | B = MatrixNetwork(A) 85 | obj = biconnected_components(B) 86 | components = biconnected_components(B;art=false, components=false).number 87 | number_of_components = biconnected_components!(B, zeros(Bool,0), zeros(Int64,0)) 88 | @test components == number_of_components == 1 89 | end 90 | -------------------------------------------------------------------------------- /src/dfs.jl: -------------------------------------------------------------------------------- 1 | """ 2 | DFS 3 | --- 4 | compute depth first search distances and returns the distance (d), the discover (dt), 5 | the finish time(ft), and the predecessor array (pred) in the tuple (d,dt,ft, pred).\n 6 | pred[i] = 0 if vertex i is in a component not reachable from u and i != u. 7 | 8 | Functions 9 | --------- 10 | - (d,dt,ft,pred) = dfs(A::MatrixNetwork,u::Int64,full::Int64,target::Int64) 11 | - (d,dt,ft,pred) = dfs(A::MatrixNetwork,u::Int64) 12 | - (d,dt,ft,pred) = dfs{T}(A::SparseMatrixCSC{T,Int64},u::Int64,full::Int64,target::Int64) 13 | - (d,dt,ft,pred) = dfs{T}(A::SparseMatrixCSC{T,Int64},u::Int64) 14 | - (d,dt,ft,pred) = dfs(ei::Vector{Int64},ej::Vector{Int64},u::Int64,full::Int64,target::Int64) 15 | - (d,dt,ft,pred) = dfs(ei::Vector{Int64},ej::Vector{Int64},u::Int64) 16 | 17 | Example 18 | ------- 19 | ~~~ 20 | A = load_matrix_network("dfs_example") 21 | (d,dt,ft,pred) = dfs(A,1) 22 | ~~~ 23 | """ 24 | function dfs(A::MatrixNetwork,u::Int64,full::Int64,target::Int64) 25 | 26 | (rp,ci) = (A.rp,A.ci) 27 | n=length(rp)-1 28 | d=-1*ones(Int64,n) 29 | dt=-1*ones(Int64,n) 30 | ft=-1*ones(Int64,n) 31 | pred=zeros(Int64,n) 32 | rs=zeros(Int64,2*n) 33 | rss=0 # recursion stack holds two nums (v,ri) 34 | 35 | #start dfs at u 36 | t=0 37 | targethit=0 38 | for i=1:n 39 | if i==1 40 | v=u 41 | else 42 | v=mod(u+i-1,n)+1 43 | if d[v]>0 44 | continue 45 | end 46 | end 47 | d[v]=0 48 | dt[v]=t 49 | t=t+1 50 | ri=rp[v] 51 | rss=rss+1 52 | rs[2*rss-1]=v 53 | rs[2*rss]=ri # add v to the stack 54 | 55 | while rss>0 56 | v=rs[2*rss-1] 57 | ri=rs[2*rss] 58 | rss=rss-1 # pop v from the stack 59 | if v==target || targethit == 1 60 | ri=rp[v+1] 61 | targethit=1 # end the algorithm if v is the target 62 | end 63 | while rieps()] = 1 ./rn[rn.>eps()] # do division once 51 | 52 | si = zeros(Int64,m*K) 53 | sj = zeros(Int64,m*K) 54 | sv = zeros(Float64,m*K) 55 | nused = 0 56 | 57 | dwork = zeros(Float64,m) 58 | iwork = zeros(Int64,m) 59 | iused = zeros(Int64,m) 60 | 61 | for i = 1:m 62 | 63 | # for each row of A, compute an inner product against all rows of A. 64 | # to do this efficiently, we know that the inner-product will 65 | # only be non-zero if two rows of A overlap in a column. Thus, in 66 | # row i of A, we look at all non-zero columns, and then look at all 67 | # rows touched by those columns in the A' matrix. We compute 68 | # the similarity metric, then sort and only store the top k. 69 | 70 | curused = 0 # track how many non-zeros we compute during this operation 71 | for ri = rp[i]:rp[i+1]-1 72 | # find all columns j in row i 73 | j = ci[ri] 74 | aij = ai[ri]*rn[i] 75 | for rit = rpt[j]:rpt[j+1]-1 76 | # find all rows k in column j 77 | k = cit[rit] 78 | if k == i 79 | continue 80 | end # skip over the standard entries 81 | akj = ait[rit]*rn[k] 82 | if iwork[k]>0 83 | # we already have a non-zero between row i and row k 84 | dwork[iwork[k]] += aij*akj 85 | else 86 | # we need to add a non-zero betwen row i and row k 87 | curused = curused + 1 88 | iwork[k] = curused 89 | dwork[curused] = aij*akj 90 | iused[curused] = k 91 | end 92 | end 93 | end 94 | 95 | # don't sort if fewer than K elements 96 | if curused < K 97 | sperm = 1:curused 98 | else 99 | sperm = sortperm(dwork[1:curused],rev=true) 100 | end 101 | 102 | for k = 1:min(K,length(sperm)) 103 | nused = nused + 1 104 | si[nused] = i 105 | sj[nused] = iused[sperm[k]] 106 | sv[nused] = dwork[sperm[k]] 107 | end 108 | 109 | # reset the iwork array, no need to reset dwork, as it's just 110 | # overwritten 111 | for k = 1:curused 112 | iwork[iused[k]] = 0 113 | end 114 | end 115 | 116 | ei = si[1:nused] 117 | ej = sj[1:nused] 118 | ev = sv[1:nused] 119 | 120 | S = sparse(ei,ej,ev,m,m) 121 | 122 | return S 123 | end 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Result](https://travis-ci.org/nassarhuda/MatrixNetworks.jl.svg?branch=master)](https://travis-ci.org/nassarhuda/MatrixNetworks.jl) 2 | [![codecov.io](http://codecov.io/github/nassarhuda/MatrixNetworks.jl/coverage.svg?branch=master)](http://codecov.io/github/nassarhuda/MatrixNetworks.jl?branch=master) 3 | [![DOI](https://zenodo.org/badge/37493786.svg)](https://zenodo.org/badge/latestdoi/37493786) 4 | 5 | 6 | # MatrixNetworks 7 | This package consists of a collection of network algorithms. 8 | In short, the major difference between MatrixNetworks.jl and packages like LightGraphs.jl or Graphs.jl is the way graphs are treated. 9 | 10 | In [LightGraphs.jl](https://github.com/JuliaGraphs/LightGraphs.jl), graphs are created through Graph() and DiGraph() which are based on the representation of G as G = (V,E). Similar types exist in [Graphs.jl](https://github.com/JuliaLang/Graphs.jl) (EdgeList, AdjacencyList, IncidenceList, Graph) - this is again based on viewing a graph G as a set of nodes and edges. Our viewpoint is different. 11 | 12 | MatrixNetworks is based on the philosophy that there should be no distinction between a matrix and a network - thus the name. 13 | 14 | For example, `d,dt,p = bfs(A,1)` computes the bfs distance from the node represented by row 1 to all other nodes of the graph with adjacency matrix A. (A can be of type `SparseMatrixCSC` or `MatrixNetwork`). This representation can be easier to work with and handle. 15 | 16 | The package provides documentation with sample runs for all functions - viewable through Juila’s REPL. These sample runs come with sample data, which makes it easier for users to get started on `MatrixNetworks`. 17 | 18 | 19 | ## Package Installation: 20 | ##### To install package 21 | ``` 22 | using Pkg 23 | Pkg.add("MatrixNetworks") 24 | using MatrixNetworks 25 | ``` 26 | 27 | ##### Example 28 | ``` 29 | ?bfs 30 | ?bipartite_matching 31 | ``` 32 | 33 | ##### To run test cases: 34 | ``` 35 | Pkg.test("MatrixNetworks") 36 | ``` 37 | ## Data available: 38 | ##### For a full list of all datasets: 39 | ``` 40 | matrix_network_datasets() 41 | ``` 42 | ##### Loading data example: 43 | ``` 44 | load_matrix_network("clique-10") 45 | ``` 46 | 47 | ## Some examples: 48 | ##### largest_component: Return the largest connected component of a graph 49 | Acc is a sparse matrix containing the largest connected piece of a directed graph A 50 | p is a logical vector indicating which vertices in A were chosen 51 | ``` 52 | A = load_matrix_network("dfs_example") 53 | Acc,p = largest_component(A) 54 | ``` 55 | 56 | ##### clustercoeffs: Compute undirected clustering coefficients for a graph 57 | cc is the clustering coefficients 58 | ``` 59 | A = load_matrix_network("clique-10") 60 | cc = clustercoeffs(MatrixNetwork(A)) 61 | ``` 62 | 63 | ##### bfs: Compute breadth first search distances starting from a node in a graph 64 | d is a vector containing the distances of all nodes from node u (1 in the example below) 65 | dt is a vector containing the discover times of all the nodes 66 | pred is a vector containing the predecessors of each of the nodes 67 | ``` 68 | A = load_matrix_network("bfs_example") 69 | d,dt,pred = bfs(A,1) 70 | ``` 71 | 72 | ##### scomponents: Compute the strongly connected components of a graph 73 | ``` 74 | A = load_matrix_network("cores_example") 75 | sc = scomponents(A) 76 | sc.number #number of connected componenets 77 | sc.sizes #sizes of components 78 | sc.map #the mapping of the graph nodes to their respective connected component 79 | strong_components_map(A) # if you just want the map 80 | sc_enrich = enrich(sc) # produce additional enriched output includes: 81 | sc_enrich.reduction_matrix 82 | sc_enrich.transitive_map 83 | sc_enrich.transitive_order 84 | ``` 85 | Can work on ei,ej: 86 | ``` 87 | ei = [1;2;3] 88 | ej = [2;4;1] 89 | scomponents(ei,ej) 90 | ``` 91 | 92 | ##### bipartite_matching: Return a maximum weight bipartite matching of a graph 93 | ``` 94 | ei = [1;2;3] 95 | ej = [3;2;4] 96 | BM = bipartite_matching([10;12;13],ei,ej) 97 | BM.weight 98 | BM.cardinality 99 | BM.match 100 | create_sparse(BM) # get the sparse matrix 101 | edge_list(BM) # get the edgelist 102 | edge_indicator(BM,ei,ej) # get edge indicators 103 | ``` 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/clustercoeffs.jl: -------------------------------------------------------------------------------- 1 | """ 2 | CLUSTERCOEFFS 3 | ------------- 4 | compute undirected clustering coefficients for a graph. clustercoeffs(A) computes a 5 | normalized, weighted clustering coefficients from a graph represented by a symmetric 6 | adjacency matrix A. clustercoeffs(A,weighted,normalized), with weighted and normalized 7 | boolean values indicating whether the computation has to be weighted and/or normalized. 8 | 9 | Functions 10 | --------- 11 | - cc = clustercoeffs(A::MatrixNetwork,weighted::Bool,normalized::Bool) 12 | - cc = clustercoeffs{T}(A::SparseMatrixCSC{T,Int64},weighted::Bool,normalized::Bool)\n 13 | If weighted and normalized are not specified, they are understood as true 14 | 15 | Example 16 | ------- 17 | ~~~ 18 | A = load_matrix_network("clique-10") 19 | cc = clustercoeffs(MatrixNetwork(A)) 20 | ~~~ 21 | """ 22 | function clustercoeffs(A::MatrixNetwork) 23 | return clustercoeffs(A, true, true); 24 | end 25 | 26 | function clustercoeffs(A::MatrixNetwork,weighted::Bool,normalized::Bool) 27 | donorm = true 28 | usew = true 29 | if !normalized 30 | donorm = false 31 | end 32 | if !weighted 33 | usew = false 34 | end 35 | 36 | if is_undirected(A) == false 37 | error("Only undirected (symmetric) inputs are allowed") 38 | end 39 | 40 | (rp,ci,ai) = (A.rp,A.ci,A.vals) 41 | if typeof(findfirst(ai.<0)) != Nothing 42 | error("only positive edge weights allowed") 43 | end 44 | return clustercoeffs_phase2(donorm,rp,ci,ai,usew) 45 | end 46 | 47 | function clustercoeffs_phase2(donorm::Bool,rp::Vector{Int64},ci::Vector{Int64}, 48 | ai::Vector{T}, usew::Bool) where T 49 | n = length(rp) - 1 50 | cc = Vector{Float64}(undef,n) 51 | ind = zeros(Bool,n) 52 | cache = zeros(Float64,usew ? n : 0) 53 | 54 | @inbounds for v = 1:n 55 | 56 | for rpi = rp[v]:rp[v+1]-1 57 | w = ci[rpi] 58 | if v != w 59 | ind[w] = 1 60 | if usew # as of 2016-10-04, this makes a/insignificant slowdown 61 | cache[w] = ai[rpi]^(1/3.0) 62 | end 63 | end 64 | end 65 | curcc = 0.0 66 | d = rp[v+1]-rp[v] 67 | # run two steps of bfs to try and find triangles. 68 | for rpi = rp[v]:rp[v+1]-1 69 | w = ci[rpi] 70 | if v == w 71 | d = d-1 72 | continue 73 | end #discount self-loop 74 | 75 | istart=rp[w] 76 | iend = rp[w+1]-1 77 | if usew # as of 2016-10-04, this arrangement with outer if was better 78 | for rpi2 = istart:iend 79 | x = ci[rpi2] 80 | if ind[x] 81 | curcc += ai[rpi]^(1/3)*ai[rpi2]^(1/3)*cache[x]*(x != w) 82 | end 83 | end 84 | else 85 | for rpi2 = istart:iend 86 | x = ci[rpi2] 87 | if ind[x] # 88 | curcc += 1.0*(x != w) 89 | end 90 | end 91 | end 92 | #= 93 | for rpi2 = rp[w]:rp[w+1]-1 94 | x = ci[rpi2] 95 | #if x == w 96 | #continue 97 | #end 98 | if ind[x] 99 | #if usew 100 | #curcc += ai[rpi]^(1/3)*ai[rpi2]^(1/3)*cache[x] 101 | #else 102 | curcc += 1.0*(x != w) 103 | #end 104 | end 105 | end 106 | =# 107 | end 108 | if donorm && d>1 109 | cc[v] = curcc/(d*(d-1)) 110 | elseif d>1 111 | cc[v] = curcc 112 | end 113 | 114 | for rpi = rp[v]:rp[v+1]-1 115 | ind[ci[rpi]] = 0 116 | end # reset indicator 117 | end 118 | return cc 119 | end 120 | 121 | ############################ 122 | ### Additional functions ### 123 | ############################ 124 | # sparse matrices: 125 | function clustercoeffs(A::SparseMatrixCSC{T,Int64},weighted::Bool,normalized::Bool) where T 126 | donorm = true 127 | usew = true 128 | if !normalized 129 | donorm = false 130 | end 131 | if !weighted 132 | usew = false 133 | end 134 | 135 | if !is_undirected(A) 136 | error("Only undirected (symmetric) inputs are allowed") 137 | end 138 | 139 | (rp,ci,ai) = (A.colptr,A.rowval,A.nzval) 140 | if typeof(findfirst(ai.<0)) != Nothing 141 | error("only positive edge weights allowed") 142 | end 143 | return clustercoeffs_phase2(donorm,rp,ci,ai,usew) 144 | end 145 | 146 | function clustercoeffs(A::SparseMatrixCSC{T,Int64}) where T 147 | return clustercoeffs(A, true, true); 148 | end 149 | 150 | ## Triplet Format: 151 | function clustercoeffs(ei::Vector{Int64},ej::Vector{Int64}) 152 | return clustercoeffs(MatrixNetwork(ei,ej)) 153 | end 154 | 155 | function clustercoeffs(ei::Vector{Int64},ej::Vector{Int64},weighted::Bool,normalized::Bool) 156 | return clustercoeffs(MatrixNetwork(ei,ej),weighted,normalized) 157 | end 158 | 159 | ## CSR sparse matrices - basically just like type MatrixNetwork 160 | function clustercoeffs(rp::Vector{Int64},ci::Vector{Int64},vals::Vector{T},n::Int64) where T 161 | return clustercoeffs(MatrixNetwork(n,rp,ci,vals)) 162 | end 163 | 164 | function clustercoeffs(rp::Vector{Int64},ci::Vector{Int64},vals::Vector{T},n::Int64,weighted::Bool,normalized::Bool) where T 165 | return clustercoeffs(MatrixNetwork(n,rp,ci,vals),weighted,normalized) 166 | end -------------------------------------------------------------------------------- /data/Buck.smat: -------------------------------------------------------------------------------- 1 | 90 90 338 2 | 1 0 1 3 | 5 0 1 4 | 7 0 1 5 | 0 1 1 6 | 48 1 1 7 | 3 2 1 8 | 47 2 1 9 | 48 2 1 10 | 2 3 1 11 | 4 3 1 12 | 5 3 1 13 | 3 4 1 14 | 6 4 1 15 | 45 4 1 16 | 46 4 1 17 | 0 5 1 18 | 3 5 1 19 | 6 5 1 20 | 8 5 1 21 | 4 6 1 22 | 5 6 1 23 | 9 6 1 24 | 44 6 1 25 | 0 7 1 26 | 8 7 1 27 | 11 7 1 28 | 5 8 1 29 | 7 8 1 30 | 9 8 1 31 | 10 8 1 32 | 6 9 1 33 | 8 9 1 34 | 13 9 1 35 | 43 9 1 36 | 8 10 1 37 | 11 10 1 38 | 13 10 1 39 | 7 11 1 40 | 10 11 1 41 | 12 11 1 42 | 13 11 1 43 | 11 12 1 44 | 13 12 1 45 | 14 12 1 46 | 15 12 1 47 | 9 13 1 48 | 10 13 1 49 | 11 13 1 50 | 12 13 1 51 | 14 13 1 52 | 16 13 1 53 | 42 13 1 54 | 12 14 1 55 | 13 14 1 56 | 15 14 1 57 | 16 14 1 58 | 22 14 1 59 | 12 15 1 60 | 14 15 1 61 | 22 15 1 62 | 13 16 1 63 | 14 16 1 64 | 17 16 1 65 | 20 16 1 66 | 22 16 1 67 | 16 17 1 68 | 18 17 1 69 | 19 17 1 70 | 20 17 1 71 | 42 17 1 72 | 17 18 1 73 | 21 18 1 74 | 41 18 1 75 | 17 19 1 76 | 21 19 1 77 | 23 19 1 78 | 16 20 1 79 | 17 20 1 80 | 22 20 1 81 | 18 21 1 82 | 19 21 1 83 | 24 21 1 84 | 25 21 1 85 | 14 22 1 86 | 15 22 1 87 | 16 22 1 88 | 20 22 1 89 | 19 23 1 90 | 25 23 1 91 | 33 23 1 92 | 21 24 1 93 | 26 24 1 94 | 41 24 1 95 | 21 25 1 96 | 23 25 1 97 | 27 25 1 98 | 32 25 1 99 | 24 26 1 100 | 27 26 1 101 | 28 26 1 102 | 25 27 1 103 | 26 27 1 104 | 29 27 1 105 | 30 27 1 106 | 31 27 1 107 | 26 28 1 108 | 29 28 1 109 | 34 28 1 110 | 27 29 1 111 | 28 29 1 112 | 30 29 1 113 | 34 29 1 114 | 27 30 1 115 | 29 30 1 116 | 34 30 1 117 | 27 31 1 118 | 32 31 1 119 | 35 31 1 120 | 36 31 1 121 | 25 32 1 122 | 31 32 1 123 | 33 32 1 124 | 35 32 1 125 | 40 32 1 126 | 23 33 1 127 | 32 33 1 128 | 40 33 1 129 | 28 34 1 130 | 29 34 1 131 | 30 34 1 132 | 31 35 1 133 | 32 35 1 134 | 37 35 1 135 | 38 35 1 136 | 40 35 1 137 | 31 36 1 138 | 37 36 1 139 | 39 36 1 140 | 35 37 1 141 | 36 37 1 142 | 38 37 1 143 | 39 37 1 144 | 35 38 1 145 | 37 38 1 146 | 39 38 1 147 | 36 39 1 148 | 37 39 1 149 | 38 39 1 150 | 32 40 1 151 | 33 40 1 152 | 35 40 1 153 | 18 41 1 154 | 24 41 1 155 | 42 41 1 156 | 67 41 1 157 | 73 41 1 158 | 13 42 1 159 | 17 42 1 160 | 41 42 1 161 | 43 42 1 162 | 62 42 1 163 | 66 42 1 164 | 9 43 1 165 | 42 43 1 166 | 44 43 1 167 | 58 43 1 168 | 6 44 1 169 | 43 44 1 170 | 45 44 1 171 | 55 44 1 172 | 4 45 1 173 | 44 45 1 174 | 46 45 1 175 | 53 45 1 176 | 4 46 1 177 | 45 46 1 178 | 47 46 1 179 | 53 46 1 180 | 2 47 1 181 | 46 47 1 182 | 48 47 1 183 | 51 47 1 184 | 1 48 1 185 | 2 48 1 186 | 47 48 1 187 | 50 48 1 188 | 51 48 1 189 | 50 49 1 190 | 54 49 1 191 | 56 49 1 192 | 48 50 1 193 | 49 50 1 194 | 47 51 1 195 | 48 51 1 196 | 52 51 1 197 | 51 52 1 198 | 53 52 1 199 | 54 52 1 200 | 45 53 1 201 | 46 53 1 202 | 52 53 1 203 | 55 53 1 204 | 49 54 1 205 | 52 54 1 206 | 55 54 1 207 | 57 54 1 208 | 44 55 1 209 | 53 55 1 210 | 54 55 1 211 | 58 55 1 212 | 49 56 1 213 | 57 56 1 214 | 60 56 1 215 | 54 57 1 216 | 56 57 1 217 | 58 57 1 218 | 59 57 1 219 | 43 58 1 220 | 55 58 1 221 | 57 58 1 222 | 62 58 1 223 | 57 59 1 224 | 60 59 1 225 | 62 59 1 226 | 56 60 1 227 | 59 60 1 228 | 61 60 1 229 | 62 60 1 230 | 60 61 1 231 | 62 61 1 232 | 63 61 1 233 | 64 61 1 234 | 42 62 1 235 | 58 62 1 236 | 59 62 1 237 | 60 62 1 238 | 61 62 1 239 | 63 62 1 240 | 65 62 1 241 | 61 63 1 242 | 62 63 1 243 | 64 63 1 244 | 65 63 1 245 | 71 63 1 246 | 61 64 1 247 | 63 64 1 248 | 71 64 1 249 | 62 65 1 250 | 63 65 1 251 | 66 65 1 252 | 69 65 1 253 | 71 65 1 254 | 42 66 1 255 | 65 66 1 256 | 67 66 1 257 | 68 66 1 258 | 69 66 1 259 | 41 67 1 260 | 66 67 1 261 | 70 67 1 262 | 66 68 1 263 | 70 68 1 264 | 72 68 1 265 | 65 69 1 266 | 66 69 1 267 | 71 69 1 268 | 67 70 1 269 | 68 70 1 270 | 73 70 1 271 | 74 70 1 272 | 63 71 1 273 | 64 71 1 274 | 65 71 1 275 | 69 71 1 276 | 68 72 1 277 | 74 72 1 278 | 82 72 1 279 | 41 73 1 280 | 70 73 1 281 | 75 73 1 282 | 70 74 1 283 | 72 74 1 284 | 76 74 1 285 | 81 74 1 286 | 73 75 1 287 | 76 75 1 288 | 77 75 1 289 | 74 76 1 290 | 75 76 1 291 | 78 76 1 292 | 79 76 1 293 | 80 76 1 294 | 75 77 1 295 | 78 77 1 296 | 83 77 1 297 | 76 78 1 298 | 77 78 1 299 | 79 78 1 300 | 83 78 1 301 | 76 79 1 302 | 78 79 1 303 | 83 79 1 304 | 76 80 1 305 | 81 80 1 306 | 84 80 1 307 | 85 80 1 308 | 74 81 1 309 | 80 81 1 310 | 82 81 1 311 | 84 81 1 312 | 89 81 1 313 | 72 82 1 314 | 81 82 1 315 | 89 82 1 316 | 77 83 1 317 | 78 83 1 318 | 79 83 1 319 | 80 84 1 320 | 81 84 1 321 | 86 84 1 322 | 87 84 1 323 | 89 84 1 324 | 80 85 1 325 | 86 85 1 326 | 88 85 1 327 | 84 86 1 328 | 85 86 1 329 | 87 86 1 330 | 88 86 1 331 | 84 87 1 332 | 86 87 1 333 | 88 87 1 334 | 85 88 1 335 | 86 88 1 336 | 87 88 1 337 | 81 89 1 338 | 82 89 1 339 | 84 89 1 340 | -------------------------------------------------------------------------------- /test/generators_test.jl: -------------------------------------------------------------------------------- 1 | using LinearAlgebra 2 | 3 | @testset "generators" begin 4 | @testset "erdos_renyi" begin 5 | @test_throws DomainError erdos_renyi_undirected(10,11.) 6 | @test_throws DomainError erdos_renyi_directed(10,11.) 7 | 8 | n = 100 9 | avgdegs = range(1., stop=2*log(n), length=100) 10 | compsizes = map( (dbar) -> 11 | maximum(scomponents(erdos_renyi_undirected(n,dbar)).sizes), 12 | avgdegs ) 13 | 14 | @test is_undirected(erdos_renyi_undirected(10,2.)) 15 | 16 | erdos_renyi_undirected(0, 0.) 17 | erdos_renyi_undirected(0, 0) 18 | erdos_renyi_undirected(5, 0) 19 | erdos_renyi_undirected(5, 0.) 20 | erdos_renyi_undirected(5, 1.) 21 | erdos_renyi_undirected(5, 1) 22 | @test all(diag(sparse_transpose(erdos_renyi_undirected(10, 0.5))) .== 0.) 23 | erdos_renyi_directed(0, 0) 24 | erdos_renyi_directed(0, 0) 25 | erdos_renyi_directed(5, 0.) 26 | erdos_renyi_directed(5, 1.) 27 | @test all(diag(sparse_transpose(erdos_renyi_directed(10, 0.5))) .== 0.) 28 | end 29 | 30 | @testset "chung_lu" begin 31 | @test_throws ArgumentError chung_lu_undirected([1,2],2) 32 | @test_throws ArgumentError chung_lu_undirected([1,3]) 33 | @test size(chung_lu_undirected([1,2]),1) == 2 34 | end 35 | 36 | @testset "havel_hakimi" begin 37 | @test is_graphical_sequence([1,1]) == true 38 | @test is_graphical_sequence([0,1,1]) == true 39 | @test is_graphical_sequence([2,2,2]) == true 40 | @test is_graphical_sequence([1,1,2]) == true 41 | @test is_graphical_sequence([1,2,2]) == false 42 | @test is_graphical_sequence([1,1,3]) == false 43 | @test is_graphical_sequence([0]) == true 44 | @test is_graphical_sequence(Int[]) == true 45 | @test is_graphical_sequence([1,1,1,1]) == true 46 | 47 | @test Matrix(sparse_transpose(havel_hakimi_graph(Int[]))) == zeros(Bool,0,0) 48 | @test Matrix(sparse_transpose(havel_hakimi_graph([0]))) == zeros(Bool,1,1) 49 | @test Matrix(sparse_transpose(havel_hakimi_graph([1,1]))) == [0 1; 1 0] 50 | @test Matrix(sparse_transpose(havel_hakimi_graph([2,2,2]))) == [0 1 1; 1 0 1; 1 1 0] 51 | six_star = [0 1 1 1 1 1; 1 0 0 0 0 0; 1 0 0 0 0 0; 1 0 0 0 0 0; 1 0 0 0 0 0; 1 0 0 0 0 0] 52 | @test Matrix(sparse_transpose(havel_hakimi_graph([5,1,1,1,1,1]))) == six_star 53 | 54 | @test_throws ArgumentError is_graphical_sequence([-1]) 55 | @test_throws ArgumentError havel_hakimi_graph([1,2,2]) 56 | end 57 | 58 | @testset "pa_graph" begin 59 | @test typeof(pa_graph(10,5,5)) == MatrixNetwork{Bool} 60 | @test typeof(pa_edges!(2,1,[(1,1)])) == Vector{Tuple{Int,Int}} 61 | @test_throws ArgumentError pa_edges!(5,2,Vector{Tuple{Int,Int}}()) 62 | @test is_empty(pa_graph(0,0,0)) == true 63 | @test all(diag(sparse_transpose(pa_graph(10, 2, 3))) .== 0.) 64 | @test is_undirected(pa_graph(10, 12, 3)) 65 | @test_throws ArgumentError pa_graph(-1,10,3) 66 | @test_throws ArgumentError pa_graph(5,10,-3) 67 | @test maximum(map(first, pa_edges!(5,1,[(1,2),(2,1)],2))) == 7 68 | @test size(pa_graph(10,5,5),1) == 10 69 | @test size(pa_graph(10,-5,5),1) == 10 70 | @test nnz(sparse_transpose(pa_graph(10,-5,5))) == 20 71 | end 72 | 73 | @testset "gpa_graph" begin 74 | @test typeof(gpa_graph(10,0.5,0.3,2)) == MatrixNetwork{Bool} 75 | @test typeof(gpa_graph(10,0.5,0.3,2,Val{true})) == MatrixNetwork{Bool} 76 | @test typeof(gpa_edges!(4,.75,.25,[(1,2)],1)) == Vector{Tuple{Int,Int}} 77 | @test_throws ArgumentError gpa_edges!(4,.75,.25,[(1,1)],1) 78 | @test typeof(gpa_edges!(4,.75,.25,[(1,1)],1,Val{true})) == Vector{Tuple{Int,Int}} 79 | @test is_empty(gpa_graph(0,0.0,0.0,0)) == true 80 | @test all(diag(sparse_transpose(gpa_graph(10, .3,.4, 3))) .== 0.) 81 | @test is_undirected(gpa_graph(10, .2, .8,4)) 82 | @test_throws ArgumentError gpa_graph(-1,.3,.4,5) 83 | @test_throws ArgumentError gpa_graph(8,.8,.4,2) 84 | @test_throws ArgumentError gpa_graph(10,.3,.4,-2) 85 | @test size(gpa_graph(12,.5,.4,2),1) == 12 86 | @test size(gpa_graph(21,.1,.1,3),1) == 21 87 | end 88 | 89 | @testset "roach_graph" begin 90 | @test_throws ArgumentError roach_graph(-1) 91 | @test is_empty(roach_graph(0)) 92 | @test is_connected(roach_graph(5)) 93 | @test is_connected(roach_graph(5, Val{true})[1]) 94 | @test maximum(bfs(roach_graph(5, Val{false}),20)[1]) == 11 95 | @test maximum(bfs(roach_graph(10),40)[1]) == 21 96 | @test (bfs(roach_graph(10),40)[1])[1] == 20 97 | 98 | G,xy = roach_graph(5, Val{true}) 99 | filt = vec(all(xy .>= 0,dims = 2)) 100 | Asub = sparse_transpose(G)[filt,filt] 101 | # Asub should be an antennae... 102 | @test bfs(Asub,1)[1][5] == 4 103 | filt = vec(all(xy .<= 0,dims = 2)) 104 | Asub = sparse_transpose(G)[filt,filt] 105 | # Asub should be an line... 106 | @test bfs(Asub,1)[1][5] == 4 107 | end 108 | 109 | @testset "lollipop_graph" begin 110 | @test_throws ArgumentError lollipop_graph(-1) 111 | @test_throws ArgumentError lollipop_graph(-1,-1) 112 | @test_throws ArgumentError lollipop_graph(5,-1,Val{true}) 113 | 114 | @test is_connected(lollipop_graph(5)) 115 | @test is_connected(lollipop_graph(5, Val{true})[1]) 116 | @test is_connected(lollipop_graph(5, 10)) 117 | @test is_connected(lollipop_graph(10, 5)) 118 | 119 | @test maximum(bfs(lollipop_graph(5,10),1)[1]) == 6 120 | @test maximum(bfs(lollipop_graph(10,5),1)[1]) == 11 121 | 122 | G,xy = lollipop_graph(6, 5, Val{true}) 123 | filt = vec(all(xy .<= 0, dims = 2)) 124 | Asub = sparse_transpose(G)[filt,filt] 125 | # Asub should be an tail (or a line graph 126 | @test bfs(Asub,1)[1][6] == 5 127 | Asub = sparse_transpose(G)[.~filt,.~filt] 128 | @test nnz(Asub) == 5*4 129 | 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /src/dijkstra.jl: -------------------------------------------------------------------------------- 1 | """ 2 | DIJKSTRA 3 | -------- 4 | compute shortest paths using Dijkstra's algorithm. 5 | d = dijkstra(A,u) computes the shortest path from vertex u to all nodes 6 | reachable from vertex u using Dijkstra's algorithm for the problem. 7 | The graph is given by the weighted sparse matrix A, where A(i,j) is 8 | the distance between vertex i and j. In the output vector d, 9 | the entry d[v] is the minimum distance between vertex u and vertex v. 10 | A vertex w unreachable from u has d(w)=Inf. 11 | pred is the predecessor tree to generate the actual shorest paths. 12 | In the predecessor tree pred[v] is the vertex preceeding v in the 13 | shortest path and pred[u]=0. Any unreachable vertex has pred[w]=0 as well. 14 | If your network is unweighted, then use bfs instead. 15 | 16 | Functions 17 | --------- 18 | - (d,pred) = dijkstra(A::MatrixNetwork,u::Int64) 19 | - (d,pred) = dijkstra{F}(A::SparseMatrixCSC{F,Int64},u::Int64) 20 | 21 | Example 22 | ------- 23 | ~~~ 24 | # Find the minimum travel time between Los Angeles (LAX) and 25 | # Rochester Minnesota (RST). 26 | 27 | (A,xy,labels) = load_matrix_network_metadata("airports") 28 | A = -A; # fix funny encoding of airport data 29 | lax = 247; rst = 355 30 | (d,pred) = dijkstra(A,lax) 31 | @printf("Minimum time: %d",d[rst]); #Print the path 32 | @printf("Path:") 33 | u = rst 34 | while(u != lax) 35 | @printf("%s <-- ", labels[u]) 36 | u = pred[u]; 37 | if (u == lax) 38 | @printf("%s", labels[lax]) 39 | end 40 | end 41 | ~~~ 42 | """ 43 | function dijkstra(A::MatrixNetwork,u::Int64) 44 | (rp,ci,ai) = (A.rp, A.ci, A.vals) 45 | return dijkstra_internal(rp,ci,ai,u) 46 | end 47 | 48 | function dijkstra(A::SparseMatrixCSC{F,Int64},u::Int64) where F 49 | (rp,ci,ai) = sparse_to_csr(A) 50 | return dijkstra_internal(rp,ci,ai,u) 51 | end 52 | 53 | function dijkstra_internal(rp::Vector{Int64},ci::Vector{Int64},ai::Vector{F},u::Int64) where F 54 | 55 | if any(ai.<0) 56 | error("dijkstra''s algorithm cannot handle negative edge weights") 57 | end 58 | 59 | n = length(rp) - 1 60 | d = Inf*ones(Float64,n) 61 | T = zeros(Int64,n) 62 | L = zeros(Int64,n) 63 | pred = zeros(Int64,length(rp)-1) 64 | 65 | n = 1 66 | T[n] = u 67 | L[u] = n # oops, n is now the size of the heap 68 | 69 | # enter the main dijkstra loop 70 | d[u] = 0 71 | while n > 0 72 | v = T[1] 73 | ntop = T[n] 74 | T[1] = ntop 75 | L[ntop] = 1 76 | n = n - 1 # pop the head off the heap 77 | k = 1 78 | kt = ntop # move element T[1] down the heap 79 | while true 80 | i = 2*k 81 | if i > n 82 | break 83 | end # end of heap 84 | if i == n 85 | it = T[i] # only one child, so skip 86 | else # pick the smallest child 87 | lc = T[i] 88 | rc = T[i+1] 89 | it = lc 90 | if d[rc] < d[lc] 91 | i = i+1 92 | it = rc 93 | end # right child is smaller 94 | end 95 | if d[kt] < d[it] 96 | break # at correct place, so end 97 | else 98 | T[k] = it 99 | L[it] = k 100 | T[i] = kt 101 | L[kt] = i 102 | k=i # swap 103 | end 104 | end # end heap down 105 | # for each vertex adjacent to v, relax it 106 | for ei = rp[v]:rp[v+1]-1 # ei is the edge index 107 | w = ci[ei] 108 | ew = ai[ei] # w is the target, ew is the edge weight 109 | # relax edge (v,w,ew) 110 | if d[w] > d[v] + ew 111 | d[w] = d[v] + ew 112 | pred[w] = v 113 | # check if w is in the heap 114 | k = L[w] 115 | onlyup=false 116 | if k == 0 117 | # element not in heap, only move the element up the heap 118 | n = n+1 119 | T[n] = w 120 | L[w] = n 121 | k = n 122 | kt = w 123 | onlyup = true 124 | else 125 | kt = T[k] 126 | end 127 | # update the heap, move the element down in the heap 128 | while !onlyup 129 | i = 2 * k 130 | if i > n 131 | break 132 | end # end of heap 133 | if i == n 134 | it = T[i] # only one child, so skip 135 | else # pick the smallest child 136 | lc = T[i] 137 | rc = T[i+1] 138 | it = lc 139 | if d[rc] < d[lc] 140 | i = i+1 141 | it = rc 142 | end # right child is smaller 143 | end 144 | if d[kt] < d[it] 145 | break # at correct place, so end 146 | else 147 | T[k] = it 148 | L[it] = k 149 | T[i] = kt 150 | L[kt] = i 151 | k = i # swap 152 | end 153 | end 154 | # move the element up the heap 155 | j = k 156 | tj = T[j] 157 | while j > 1 # j==1 => element at top of heap 158 | j2 = round(Int,floor(j/2)) 159 | tj2 = T[j2] # parent element 160 | if d[tj2] < d[tj] 161 | break # parent is smaller, so done 162 | else # parent is larger, so swap 163 | T[j2] = tj 164 | L[tj] = j2 165 | T[j] = tj2 166 | L[tj2] = j 167 | j=j2 168 | end 169 | end 170 | end 171 | end 172 | end 173 | return (d,pred) 174 | end 175 | 176 | -------------------------------------------------------------------------------- /src/scomponents.jl: -------------------------------------------------------------------------------- 1 | """ 2 | SCOMPONENTS 3 | ----------- 4 | compute the strongly connected components of a graph 5 | 6 | ci=scomponents(A) returns an index for the component number of every 7 | vertex in the graph A. The total number of components is maximum(ci). 8 | If the input is undirected, then this algorithm outputs just the 9 | connected components. Otherwise, it output the strongly connected components. 10 | 11 | The implementation is from Tarjan's 1972 paper: Depth-first search and 12 | linear graph algorithms. In SIAM's Journal of Computing, 1972, 1, 13 | pp.146-160. 14 | 15 | Functions 16 | --------- 17 | - `cc = scomponents(A::MatrixNetwork)` 18 | - `cc = scomponents{T}(A::SparseMatrixCSC{T,Int64})` 19 | - `sci = strong_components_map(A::MatrixNetwork)` 20 | - `sci = strong_components_map{T}(A::SparseMatrixCSC{T,Int64})` 21 | - `sc_rich = enrich(cc::Strong_components_output) # check ?enrich for more` 22 | 23 | Example 24 | ------- 25 | ~~~ 26 | A = load_matrix_network("cores_example") 27 | cc = scomponents(A) 28 | scomponents(A).number 29 | scomponents(A).sizes 30 | scomponents(A).map 31 | strong_components_map(A) # if you just want the map 32 | enrich(scomponents(A)) # produce additional enriched output 33 | 34 | # Can work on [ei,ej] 35 | ei = [1;2;3] 36 | ej = [2;4;1] 37 | cc = scomponents(ei,ej) 38 | 39 | # Can work on sparse matrix A 40 | A = sprand(5,5,0.5) 41 | cc = scomponents(A) 42 | ~~~ 43 | """ 44 | function scomponents end 45 | 46 | ########################### 47 | ## Type Definitions # 48 | ########################### 49 | mutable struct Strong_components_output 50 | map::Vector{Int64} # The indicator map 51 | sizes::Vector{Int64} # Array of sizes corresponding to map 52 | number::Int64 # Int64 53 | A::MatrixNetwork # MatrixNetwork 54 | end 55 | 56 | mutable struct Strong_components_rich_output 57 | reduction_matrix::SparseMatrixCSC{Int64,Int64} # the reduction matrix (restriction matrix) 58 | transitive_order::SparseMatrixCSC{Int64,Int64} 59 | transitive_map::SparseMatrixCSC{Int64,Int64} 60 | end 61 | 62 | #################### 63 | ## Functions # 64 | #################### 65 | 66 | 67 | """ 68 | Return information on the strongly connected components of a graph that 69 | is the minimum required computation. 70 | Example: 71 | ``strong_components_map(MatrixNetwork(sprand(5,4,0.1)))`` 72 | """ 73 | function strong_components_map(A::MatrixNetwork) 74 | 75 | # TODO, remove the dt variable here 76 | n=A.n 77 | sci=zeros(Int64,n) 78 | cn=1 79 | root=zeros(Int64,n) 80 | dt=zeros(Int64,n) 81 | t=0 82 | cs=zeros(Int64,n) 83 | css=0 # component stack 84 | rs=zeros(Int64,2*n) 85 | rp = A.rp 86 | ci = A.ci 87 | rss=0 # recursion stack holds two nums (v,ri) 88 | 89 | # start dfs at 1 90 | for sv=1:n 91 | v=sv 92 | if root[v]>0 93 | continue 94 | end 95 | rss=rss+1 96 | rs[2*rss-1]=v 97 | rs[2*rss]=rp[v] #add v to the stack 98 | root[v]=v 99 | sci[v]=-1 100 | dt[v]=t 101 | t=t+1 102 | css=css+1 103 | cs[css]=v #add w to component stack 104 | while rss>0 105 | v=rs[2*rss-1] # pop v from the stack 106 | ri=rs[2*rss] 107 | rss=rss-1 108 | while ridt[root[w]] 127 | root[v]=root[w] 128 | end 129 | end 130 | end 131 | if root[v]==v 132 | while css>0 133 | w=cs[css] 134 | css=css-1 135 | sci[w]=cn 136 | if w==v 137 | break 138 | end 139 | end 140 | cn=cn+1 141 | end 142 | end 143 | end 144 | return sci 145 | end 146 | 147 | ######################################################## 148 | ## Conversion Functions for strong_components_map # 149 | ######################################################## 150 | # CSC: 151 | strong_components_map(A::SparseMatrixCSC{T,Int64}) where {T} = 152 | strong_components_map(MatrixNetwork(A)) 153 | # Triplet: 154 | strong_components_map(ei::Vector{Int64},ej::Vector{Int64}) = 155 | strong_components_map(MatrixNetwork(ei,ej)) 156 | # CSR 157 | strong_components_map(rp::Vector{Int64},ci::Vector{Int64},vals::Vector{T},n::Int64) where {T} = 158 | strong_components_map(MatrixNetwork(n,rp,ci,vals)) 159 | 160 | ###################### 161 | ## scomponents # 162 | ###################### 163 | 164 | function scomponents(A::MatrixNetwork) 165 | mapping = strong_components_map(A) 166 | number = 0 167 | if length(mapping) > 0 168 | number = maximum(mapping) 169 | end 170 | sizes = zeros(Int64,number) 171 | for i = 1:length(mapping) 172 | sizes[mapping[i]] += 1 173 | end 174 | 175 | return Strong_components_output(mapping, sizes, number, A) 176 | end 177 | 178 | ############################### 179 | ## Conversion Functions # 180 | ############################### 181 | 182 | # CSC: 183 | scomponents(A::SparseMatrixCSC{T,Int64}) where {T} = scomponents(MatrixNetwork(A)) 184 | # Triplet: 185 | scomponents(ei::Vector{Int64},ej::Vector{Int64}) = scomponents(MatrixNetwork(ei,ej)) 186 | # CSR 187 | scomponents(rp::Vector{Int64},ci::Vector{Int64},vals::Vector{T},n::Int64) where {T} = scomponents(MatrixNetwork(n,rp,ci,vals)) 188 | 189 | """ 190 | This function adds the following helpers variables 191 | * reduction_matrix - a reduction matrix to project down to the component graph 192 | * transitive_order - a transitive ordering of the components 193 | * transitive_map - a map to components that respects the transitive ordering 194 | * largest 195 | """ 196 | function enrich(rval::Strong_components_output) 197 | ci = rval.map 198 | sizes = rval.sizes 199 | ncomp = maximum(ci) 200 | R = sparse(collect(1:rval.A.n),ci,1,rval.A.n,ncomp) 201 | A = SparseMatrixCSC(rval.A.n,rval.A.n,rval.A.rp,rval.A.ci,rval.A.vals) 202 | CG = R'*A'*R 203 | return Strong_components_rich_output(R,A,CG) 204 | end 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /src/biconnected.jl: -------------------------------------------------------------------------------- 1 | """ 2 | biconnected component decomposition 3 | ---------------------------------- 4 | 5 | Any connected graph decomposes into a tree of biconnected components 6 | called the block-cut tree of the graph. The blocks are attached to each 7 | other at shared vertices called cut vertices or articulation points. 8 | 9 | This implementation is based on the algorithm provided by Tarjan 10 | in "Depth-First Search and Linear Graph Algorithms". 11 | """ 12 | mutable struct Biconnected_components_output 13 | map::Vector{Int64} #biconnected_component_number 14 | articulation_points::Vector{Bool} 15 | number::Int64 16 | A::MatrixNetwork #MatrixNetwork 17 | end 18 | 19 | """ 20 | `biconnected_components!` 21 | --- 22 | This function returns the number of biconnected components in the 23 | underlying graph. It expects an undirected graph as its input. 24 | 25 | Functions 26 | --------- 27 | - `number = biconnected_components!(A::MatrixNetwork, articulation::Vector{Bool}, map::Vector{Int64})` 28 | 29 | Inputs 30 | ------ 31 | - `A`: the adjacency matrix. 32 | - `articulation`: A boolean array, where each element is initialized to false. 33 | - `map`: Vector of size equal to the number of edges. 34 | 35 | Returns 36 | ------- 37 | - `cn`: The number of biconnected components in the graph 38 | 39 | Example 40 | ------- 41 | A = load_matrix_network("biconnected_example") 42 | B = MatrixNetwork(A) 43 | number_of_components = biconnected_components!(B, zeros(Bool,0), zeros(Int64,0)) 44 | """ 45 | function biconnected_components!(A::MatrixNetwork, articulation::Vector{Bool}, map::Vector{Int64}) 46 | n=length(A.rp)-1 47 | rp=A.rp 48 | ci=A.ci 49 | components = length(map) >= length(ci) 50 | cn=1 51 | low=zeros(Int64,n) 52 | dt=-1*ones(Int64,n) 53 | pred=zeros(Int64,n) 54 | t=0 55 | largest_bcc=0 56 | edge_count_bcc=0 57 | rs=Tuple{Int,Int,Int}[] 58 | cs=Tuple{Int,Int,Int}[] 59 | root_children= 0 60 | art = length(articulation) >= n 61 | 62 | #start dfs at 1. 63 | for sv=1:n 64 | v=sv 65 | if dt[v]>0 66 | continue 67 | end 68 | root_children = 0 69 | low[v]=t 70 | dt[v]=t 71 | t=t+1 72 | w = ci[rp[v]] 73 | if (v == w) && (rp[v]+1 == rp[v+1]) 74 | if components 75 | map[rp[v]]=cn 76 | end 77 | cn = cn+1 78 | if art 79 | articulation[v]=1 80 | end 81 | else 82 | push!(rs, (v,v,rp[v])) #add v,rp[v] to stack 83 | end 84 | 85 | while size(rs, 1) > 0 86 | (g_parent, parent, children_index) = rs[end] 87 | child_index = children_index 88 | if child_index != rp[parent+1] 89 | child = ci[child_index] 90 | children_index = children_index+1 91 | (g_parent, parent, temp) = pop!(rs) 92 | push!(rs, (g_parent, parent, children_index)) 93 | if (g_parent == child) || (parent == child) 94 | continue # skip tree edges and self loops 95 | end 96 | if dt[child] >= 0 97 | if dt[child] <= dt[parent] 98 | if dt[child] < low[parent] #Update Low 99 | low[parent] = dt[child] 100 | end 101 | push!(cs, (parent,child,child_index)) 102 | end 103 | else 104 | low[child] = dt[child] = t 105 | t+=1 106 | push!(rs, (parent, child, rp[child])) 107 | push!(cs, (parent,child,child_index)) 108 | end 109 | else 110 | pop!(rs) 111 | if size(rs,1) > 1 112 | if low[parent] >= dt[g_parent] # g_parent is an Articulation point 113 | if art 114 | articulation[g_parent]=1 115 | end 116 | while true 117 | (u,v,child_index)=pop!(cs) 118 | if components 119 | map[child_index]=cn #assign the component number of the corresponding edge 120 | end 121 | if u == g_parent && v == parent 122 | break 123 | end 124 | end 125 | cn+=1 126 | end 127 | if low[parent] < low[g_parent] 128 | low[g_parent] = low[parent] 129 | end 130 | elseif size(rs,1) == 1 131 | root_children +=1 132 | while true 133 | (u,v,child_index) = pop!(cs) 134 | if components 135 | map[child_index]=cn 136 | end 137 | if u == g_parent && v == parent 138 | break 139 | end 140 | end 141 | cn+=1 142 | end 143 | end 144 | end 145 | end 146 | cn=cn-1 147 | 148 | return cn 149 | end 150 | 151 | 152 | """ 153 | `biconnected_components` 154 | ----------------------- 155 | This function requires a symmetric matrix as input. Depending on the user input this 156 | function returns either the biconnected component number associated with each edge 157 | or articlation points or both. 158 | 159 | Inputs 160 | --------- 161 | - `A`: the adjacency matrix 162 | - Optional Keyword inputs 163 | - `art=true`: returns the articulation points of the graph. 164 | - `components=true`:returns the biconnected component labels associated with each 165 | edge. 166 | 167 | Returns 168 | ------- 169 | - Returns a `Biconnected_components_output` type which includes 170 | `map` : biconnected component labels associated with each edge, 171 | `articulation_points`: boolean array that signifies whether a vertex is an articulation point and 172 | `number`: Number of biconnected components in the graph. 173 | 174 | Example 175 | ------- 176 | A = load_matrix_network("biconnected_example") 177 | B = MatrixNetwork(A) 178 | bcc = biconnected_components(B) 179 | map = bcc.map 180 | articulation_vector = bcc.articulation_points 181 | number_of_components = bcc.number 182 | """ 183 | function biconnected_components(A::MatrixNetwork; art::Bool = true, components::Bool = true) 184 | map = components ? zeros(Int64, length(A.ci)) : zeros(Int64, 0) 185 | articulation = art ? zeros(Bool, A.n) : zeros(Bool, 0) 186 | cn = biconnected_components!(A, articulation, map) 187 | return Biconnected_components_output(map,articulation,cn,A) 188 | end 189 | 190 | ############################### 191 | ## Conversion Functions # 192 | ############################### 193 | 194 | #CSC 195 | biconnected_components(A::SparseMatrixCSC;kwargs...) = biconnected_components(MatrixNetwork(A);kwargs...) 196 | 197 | #Triplet 198 | biconnected_components(ei::Vector{Int64},ej::Vector{Int64};kwargs...) = biconnected_components(MatrixNetwork(ei,ej);kwargs...) 199 | 200 | #CSR 201 | biconnected_components(rp::Vector{Int64},ci::Vector{Int64},vals::Vector{T},n::Int64; kwargs...) where {T} = biconnected_components(MatrixNetwork(n,rp,ci,vals);kwargs...) 202 | -------------------------------------------------------------------------------- /src/dirclustercoeffs.jl: -------------------------------------------------------------------------------- 1 | """ 2 | DIRCLUSTERCOEFFS 3 | ---------------- 4 | compute clustering coefficients for a directed graph. 5 | cc = dirclustercoeffs(A) returns the directed clustering coefficients 6 | (which generalize the clustering coefficients of an undirected graph, 7 | and so calling this function on an undirected graph will produce the same 8 | answer as clustercoeffs, but less efficiently.) 9 | 10 | This function implements the algorithm from Fagiolo, Phys Rev. E. 11 | 76026107 (doi:10:1103/PhysRevE.76.026107). 12 | 13 | (cc,cccyc,ccmid,ccin,ccout,nf) = dirclusteringcoeffs(A) returns different 14 | components of the clustering coefficients corresponding to cycles, 15 | middles, in triangles, and out triangles. See the manuscript for a 16 | description of the various types of triangles counted in the above metrics. 17 | 18 | Functions 19 | --------- 20 | - (cc,cccyc,ccmid,ccin,ccout,nf) = dirclustercoeffs{T}(A::SparseMatrixCSC{T,Int64},weighted::Bool) 21 | - (cc,cccyc,ccmid,ccin,ccout,nf) = dirclustercoeffs{T}(A::SparseMatrixCSC{T,Int64}) 22 | - (cc,cccyc,ccmid,ccin,ccout,nf) = dirclustercoeffs{T}(A::SparseMatrixCSC{T,Int64},weighted::Bool,normalized::Bool) 23 | 24 | Example 25 | ------- 26 | ~~~ 27 | (A,xy,labels) = load_matrix_network_metadata("celegans") 28 | (cc, cccyc, ccmid, ccin, ccout, nf) = dirclustercoeffs(A, true, true) 29 | (maxval, maxind) = findmax(cc) 30 | labels[maxind] 31 | ~~~ 32 | """ 33 | function dirclustercoeffs(A::SparseMatrixCSC{T,Int64},weighted::Bool) where T 34 | return dirclustercoeffs(A,weighted,true) 35 | end 36 | 37 | function dirclustercoeffs(A::SparseMatrixCSC{T,Int64}) where T 38 | return dirclustercoeffs(A,true,true) 39 | end 40 | 41 | function dirclustercoeffs(A::SparseMatrixCSC{T,Int64},weighted::Bool,normalized::Bool) where T 42 | donorm = true 43 | usew = true 44 | 45 | if !normalized 46 | donorm = false 47 | end 48 | if !weighted 49 | usew = false 50 | end 51 | 52 | if usew 53 | (rp,ci,ai) = sparse_to_csr(A) 54 | (col_ptr,ri,ati) = sparse_to_csr(copy(A')) 55 | else 56 | (rp,ci) = sparse_to_csr(A) 57 | (col_ptr,ri) = sparse_to_csr(copy(A')) 58 | end 59 | 60 | if any(ai.<0) 61 | error("only positive edge weights allowed") 62 | end 63 | 64 | n = length(rp)-1 65 | # initialize all the variables 66 | cc = zeros(Float64,n) 67 | ind = zeros(Bool,n) 68 | cache = zeros(Float64,n) 69 | degs = zeros(Int64,n) 70 | cccyc = zeros(Float64,n) 71 | ccmid = zeros(Float64,n) 72 | ccin = zeros(Float64,n) 73 | ccout = zeros(Float64,n) 74 | nf = zeros(Int64,n) 75 | 76 | # precompute degrees 77 | for v = 1:n 78 | for rpi = rp[v]:rp[v+1]-1 79 | w = ci[rpi] 80 | if v == w 81 | continue 82 | else 83 | degs[w] = degs[w] + 1 84 | degs[v] = degs[v] + 1 85 | end 86 | end 87 | end 88 | 89 | ew = one(T) 90 | ew2 = one(T) 91 | for v = 1:n 92 | # setup counts for the different cycle types 93 | bilatedges = 0.0 94 | curcccyc = 0.0 95 | curccmid = 0.0 96 | curccin = 0.0 97 | curccout = 0.0 98 | # 1. 99 | # find triangles with out links as last step, so precompute the inlinks 100 | # back to node v 101 | for cpi = col_ptr[v]:col_ptr[v+1]-1 102 | w = ri[cpi] 103 | if usew 104 | ew = ati[cpi] 105 | end 106 | if v != w 107 | ind[w] = 1 108 | cache[w] = ew^(1/3) 109 | end 110 | end 111 | # count cycles (cycles are out->out->out) 112 | for rpi = rp[v]:rp[v+1]-1 113 | w = ci[rpi] 114 | if v == w 115 | continue 116 | end # discount self-loop 117 | if usew 118 | ew = ai[rpi]^(1/3) 119 | end 120 | for rpi2 = rp[w]:rp[w+1]-1 121 | x = ci[rpi2] 122 | if x == w 123 | continue 124 | end 125 | if x == v 126 | bilatedges = bilatedges + 1 127 | continue 128 | end 129 | if ind[x] == 1 130 | if usew 131 | ew2 = ai[rpi2] 132 | end 133 | curcccyc = curcccyc + ew*ew2^(1/3) * cache[x] 134 | end 135 | end 136 | end 137 | # count middle-man circuits (out->in->out) 138 | for rpi = rp[v]:rp[v+1]-1 139 | w = ci[rpi] 140 | if v == w 141 | continue 142 | end # discount self-loop 143 | if usew 144 | ew = ai[rpi]^(1/3) 145 | end 146 | for cpi = col_ptr[w]:col_ptr[w+1]-1 147 | x=ri[cpi] 148 | if x==w 149 | continue 150 | end 151 | if ind[x] == 1 152 | if usew 153 | ew2 = ati[cpi] 154 | end 155 | curccmid=curccmid+ew*ew2^(1/3)*cache[x] 156 | end 157 | end 158 | end 159 | # count in-link circuits (in->out->out) 160 | for cpi = col_ptr[v]:col_ptr[v+1]-1 161 | w = ri[cpi] 162 | if v == w 163 | continue 164 | end # discount self-loop 165 | if usew 166 | ew = ati[cpi]^(1/3) 167 | end 168 | for rpi2 = rp[w]:rp[w+1]-1 169 | x = ci[rpi2] 170 | if x == w 171 | continue 172 | end 173 | if ind[x] == 1 174 | if usew 175 | ew2 = ai[rpi2] 176 | end 177 | curccin = curccin + ew*ew2^(1/3) * cache[x] 178 | end 179 | end 180 | end 181 | # reset and reinit the cache for outlinks 182 | for cpi = col_ptr[v]:col_ptr[v+1]-1 183 | w = ri[cpi] 184 | ind[w]=0 185 | end # reset indicator 186 | for rpi = rp[v]:rp[v+1]-1 187 | w = ci[rpi] 188 | if usew 189 | ew = ai[rpi] 190 | end 191 | if v != w 192 | ind[w] = 1 193 | cache[w] = ew^(1/3) 194 | end 195 | end 196 | # count out-link circuits (out->out->in) 197 | for rpi = rp[v]:rp[v+1]-1 198 | w = ci[rpi] 199 | if v == w 200 | continue 201 | end # discount self-loop 202 | if usew 203 | ew = ai[rpi]^(1/3) 204 | end 205 | for rpi2 = rp[w]:rp[w+1]-1 206 | x = ci[rpi2] 207 | if x == w 208 | continue 209 | end 210 | if ind[x] == 1 211 | if usew 212 | ew2 = ai[rpi2] 213 | end 214 | curccout = curccout+ew*ew2^(1/3) * cache[x] 215 | end 216 | end 217 | end 218 | for rpi = rp[v]:rp[v+1]-1 219 | w = ci[rpi] 220 | ind[w] = 0 221 | end # reset indicator 222 | # store the values 223 | curnf = degs[v]*(degs[v]-1) - 2*bilatedges 224 | curcc = curcccyc + curccmid + curccin + curccout 225 | if curnf>0 && donorm 226 | curcc = curcc/curnf 227 | end 228 | cc[v] = curcc 229 | cccyc[v] = curcccyc 230 | ccmid[v] = curccmid 231 | ccin[v] = curccin 232 | ccout[v] = curccout 233 | nf[v] = curnf 234 | 235 | end 236 | return (cc,cccyc,ccmid,ccin,ccout,nf) 237 | end 238 | -------------------------------------------------------------------------------- /src/MatrixNetwork.jl: -------------------------------------------------------------------------------- 1 | mutable struct MatrixNetwork{T} 2 | n::Int64 # number of columns/rows 3 | rp::Vector{Int64} # row pointers 4 | ci::Vector{Int64} # column indices 5 | vals::Vector{T} # corresponding values 6 | end 7 | 8 | function MatrixNetwork(A::SparseMatrixCSC{T,Int64}) where T 9 | At = copy(A') 10 | return MatrixNetwork(size(At,2),At.colptr,At.rowval,At.nzval) 11 | end 12 | 13 | function _second(x) 14 | return x[2] 15 | end 16 | 17 | MatrixNetwork(edges::Vector{Tuple{Int,Int}}, n::Int) = 18 | MatrixNetwork(map(first,edges),map(_second,edges), n) 19 | 20 | 21 | MatrixNetwork(ei::Vector{Int64},ej::Vector{Int64}) = 22 | MatrixNetwork(ei,ej,max(maximum(ei),maximum(ej))) 23 | 24 | function MatrixNetwork(ei::Vector{Int64},ej::Vector{Int64},n::Int64) 25 | At = sparse(ej,ei,true,n,n); 26 | return MatrixNetwork(size(At,2),At.colptr,At.rowval,At.nzval) 27 | end 28 | 29 | 30 | function _matrix_network_direct(A::SparseMatrixCSC{T,Int64}) where T 31 | return MatrixNetwork(size(A,2),A.colptr,A.rowval,A.nzval) 32 | end 33 | 34 | function _matrix_network_direct(A::SparseMatrixCSC{T,Int64},v) where T 35 | nzval = ones(typeof(v),length(A.nzval)) 36 | return MatrixNetwork(size(A,2),A.colptr,A.rowval,nzval) 37 | end 38 | 39 | 40 | import SparseArrays.sparse, Base.size, Base.*, LinearAlgebra.mul!, Base.convert, Base.adjoint, Base.copy 41 | 42 | """ 43 | Return back an adjacency matrix representation 44 | of the transpose. This requires no work. 45 | """ 46 | function sparse_transpose(A::MatrixNetwork{T}) where T 47 | return SparseMatrixCSC(A.n,A.n,A.rp,A.ci,A.vals) 48 | end 49 | 50 | adjoint(A::MatrixNetwork{T}) where T = Adjoint{Any,MatrixNetwork{T}}(A) 51 | 52 | """ 53 | Return back an adjacency matrix representation 54 | of the current MatrixNetwork 55 | """ 56 | function sparse(A::MatrixNetwork{T}) where T 57 | return copy(sparse_transpose(A)') 58 | end 59 | 60 | function size(A::MatrixNetwork) 61 | return (A.n,A.n) 62 | end 63 | 64 | import Base.ndims 65 | ndims(op::MatrixNetwork) = 2 66 | 67 | function size(A::MatrixNetwork, dim::Integer) 68 | if dim == 1 || dim == 2 69 | return A.n 70 | elseif dim > 2 71 | return 1 72 | else 73 | throw(DomainError(dim)) 74 | end 75 | end 76 | 77 | *(M::MatrixNetwork{T}, b::AbstractVector{S}) where {T,S} = 78 | mul!(Array{promote_type(T,S)}(undef,size(M,2)),sparse_transpose(M)',b) 79 | 80 | mul!(output,M::MatrixNetwork,b) = mul!(output,sparse_transpose(M)',b) 81 | 82 | *(M::Adjoint{<:Any,<:MatrixNetwork{T}}, b::AbstractVector{S}) where {T,S} = 83 | mul!(Array{promote_type(T,S)}(undef,size(M.parent,2)),sparse_transpose(M.parent),b) 84 | 85 | mul!(output,M::Adjoint{<:Any,<:MatrixNetwork},b) = mul!(output,sparse_transpose(M.parent),b) 86 | 87 | sparse(M::Adjoint{<:Any,<:MatrixNetwork}) = sparse_transpose(M.parent) 88 | copy(M::Adjoint{<:Any,<:MatrixNetwork}) = sparse_transpose(M.parent) 89 | 90 | """ 91 | `is_empty` 92 | ========== 93 | 94 | Return true if the graph is the empty graph and 95 | false otherwise. 96 | 97 | Functions 98 | --------- 99 | -`is_empty(A::MatrixNetwork)` 100 | 101 | Example 102 | ------- 103 | ~~~~ 104 | is_empty(MatrixNetwork(Int[],Int[],0)) 105 | is_empty(erdos_renyi_undirected(0,0)) 106 | ~~~~ 107 | """ 108 | is_empty(A::MatrixNetwork) = size(A,1) == 0 109 | 110 | """ 111 | `is_undirected` 112 | =============== 113 | 114 | Check the matrix associated with a matrix network 115 | for symmetry. 116 | 117 | Input 118 | ----- 119 | - `A`: a matrix network 120 | 121 | Returns 122 | ------- 123 | - `bool` with true indicating the network is undirected 124 | and the matrix is symmetric 125 | """ 126 | function is_undirected end 127 | 128 | function is_undirected(A::MatrixNetwork) 129 | M = sparse_transpose(A) 130 | return issymmetric(M) 131 | end 132 | 133 | function is_undirected(A::SparseMatrixCSC) 134 | return issymmetric(A) 135 | end 136 | 137 | """ 138 | `is_connected` 139 | ============== 140 | 141 | Check the matrix associated with a matrix network 142 | for (strong) connectivity 143 | 144 | Usage 145 | ----- 146 | - `is_connected(A)` 147 | 148 | Input 149 | ----- 150 | - `A`: a `MatrixNetwork` or `SparseMatrixCSC` class 151 | 152 | Returns 153 | ------- 154 | - `bool` with true indicating the matrix is strongly connected 155 | and false indicating 156 | """ 157 | function is_connected end 158 | 159 | function is_connected(A::Union{MatrixNetwork,SparseMatrixCSC}) 160 | # this is equivalent to maximum with a default value of 0 161 | return mapreduce(identity, max, strong_components_map(A); init=0) == 1 162 | end 163 | 164 | """ 165 | `empty_graph` 166 | ================ 167 | 168 | Returns an empty graph with n vertices and zero edges 169 | 170 | Functions 171 | --------- 172 | * `A = empty_graph(n)` generates an empty graph on n edges. 173 | 174 | Example 175 | ------- 176 | ~~~~ 177 | is_connected(empty_graph(0)) 178 | is_connected(empty_graph(1)) 179 | ~~~~ 180 | """ 181 | function empty_graph end 182 | 183 | function empty_graph(n::Integer=0) 184 | return MatrixNetwork(n,ones(Int64,n+1),Array{Int64}(undef, 0),Array{Float64}(undef, 0)) 185 | end 186 | 187 | """ 188 | `random_edge` 189 | ============= 190 | Identify a random edge of a matrix network or sparse matrix. 191 | 192 | Functions 193 | --------- 194 | * `random_edge(A::MatrixNetwork) -> (ei,ej,ind)` 195 | gets a random edge/non-zero from the matrix 196 | 197 | * `random_edge(A::SparseMatrixCSC) -> (ei,ej,ind)` 198 | gets a random non-zero from the matrix 199 | 200 | Example 201 | ------- 202 | ~~~~ 203 | G = lollipop_graph(5,3) 204 | # count the number of edges we randomly see between the regions 205 | C = Dict{Symbol,Int}() 206 | M = zeros(8,8) 207 | for i=1:1000000 208 | ei,ej = MatrixNetwork.random_edge(G) 209 | M[ei,ej] += 1 210 | if 1 <= ei <= 5 && 1 <= ej <= 5 211 | C[:Stem] = get(C, :Stem, 0) + 1 212 | elseif 6 <= ei <= 10 && 6 <= ej <= 10 213 | C[:Pop] = get(C, :Pop, 0) + 1 214 | else 215 | C[:Bridge] = get(C, :Bridge, 0) + 1 216 | end 217 | end 218 | # 4 edges in stem, 3 edges in pop, 1 edge in bridge 219 | @show C 220 | @show M 221 | ~~~~ 222 | """ 223 | function random_edge(A::MatrixNetwork) 224 | ind = rand(1:length(A.ci)) # the index 225 | ej = A.ci[ind] 226 | ei = searchsortedlast(A.rp, ind) # uses binary search for efficiency 227 | @assert ei <= A.n 228 | return (ei,ej,ind) 229 | end 230 | function random_edge(A::SparseMatrixCSC) 231 | ind = rand(1:length(A.rowval)) # the index 232 | ei = A.rowval[ind] 233 | ej = searchsortedlast(A.colptr, ind) # uses binary search for efficiency 234 | @assert ej <= A.n 235 | return (ei,ej,ind) 236 | end 237 | 238 | """ 239 | undirected_edges(A) -> srcs,dsts 240 | 241 | Produce lists just for the undirected edges. This assumes you want only 242 | those edges where (target >= source). 243 | """ 244 | function undirected_edges(A::MatrixNetwork) 245 | ei = Vector{Int64}() 246 | ej = Vector{Int64}() 247 | sizehint!(ei, div(A.rp[A.n+1],2)) 248 | sizehint!(ej, div(A.rp[A.n+1],2)) 249 | for i=1:A.n 250 | for nzi=A.rp[i]:A.rp[i+1]-1 251 | j = A.ci[nzi] 252 | if j >= i 253 | push!(ei, i) 254 | push!(ej, j) 255 | end 256 | end 257 | end 258 | return ei,ej 259 | end 260 | 261 | """ 262 | directed_edges(A) -> srcs,dsts 263 | 264 | Produce lists just for all edges of the graph, including both sides for 265 | undirected edges. This is essentially the same as findnz for a sparse matrix, 266 | optimized not to return the values. 267 | """ 268 | function directed_edges(A::MatrixNetwork) 269 | ei = Vector{Int64}() 270 | ej = Vector{Int64}() 271 | sizehint!(ei, A.rp[A.n+1]) 272 | sizehint!(ej, A.rp[A.n+1]) 273 | for i=1:A.n 274 | for nzi=A.rp[i]:A.rp[i+1]-1 275 | j = A.ci[nzi] 276 | push!(ei, i) 277 | push!(ej, j) 278 | end 279 | end 280 | return ei,ej 281 | end 282 | 283 | -------------------------------------------------------------------------------- /src/mst_prim.jl: -------------------------------------------------------------------------------- 1 | """ 2 | MST_PRIM 3 | -------- 4 | compute a minimum spanning tree with Prim's algorithm. 5 | T = mst_prim_matrix(A) computes a minimum spanning tree T using Prim's algorithm 6 | for the spanning tree of a graph with non-negative edge weights. 7 | 8 | T = mst_prim_matrix(A,false) produces an MST for just the component at A containing 9 | vertex 1. T = mst_prim_matrix(A,0,u) produces the MST for the component 10 | containing vertex u. 11 | 12 | (ti,tj,tv,nverts) = mst_prim(A) returns the edges from the matrix and does not 13 | convert to a sparse matrix structure. This saves a bit of work and is 14 | required when there are 0 edge weights. 15 | 16 | Functions 17 | --------- 18 | - (ti,tj,tv,nverts) = mst_prim(A::MatrixNetwork,full::Bool,u::Int64) 19 | - (ti,tj,tv,nverts) = mst_prim(A::MatrixNetwork) 20 | - (ti,tj,tv,nverts) = mst_prim{T}(A::SparseMatrixCSC{T,Int64},full::Bool,u::Int64) 21 | - (ti,tj,tv,nverts) = mst_prim{T}(A::SparseMatrixCSC{T,Int64}) 22 | - T = mst_prim_matrix(A::MatrixNetwork,full::Bool,u::Int64) #output modifier 23 | - T = mst_prim_matrix(A::MatrixNetwork) #output modifier 24 | - T = mst_prim_matrix{T}(A::SparseMatrixCSC{T,Int64},full::Bool,u::Int64) #output modifier 25 | - T = mst_prim_matrix{T}(A::SparseMatrixCSC{T,Int64}) #output modifier 26 | 27 | Example 28 | ------- 29 | ~~~ 30 | A = load_matrix_network("airports") 31 | A = -A #convert to travel time 32 | A = max.(A,A') 33 | A = sparse(A) 34 | (ti,tj,tv,nverts) = mst_prim(A) 35 | ~~~ 36 | """ 37 | function mst_prim(A::MatrixNetwork,full::Bool,u::Int64) 38 | 39 | (rp,ci,ai) = (A.rp,A.ci,A.vals) 40 | if any(ai.<0) 41 | error("prim''s algorithm cannot handle negative edge weights.") 42 | end 43 | nverts = length(rp) - 1 44 | d = Inf*ones(Float64,nverts) 45 | T = zeros(Int64,nverts) 46 | L = zeros(Int64,nverts) 47 | pred = zeros(Int64,length(rp)-1) 48 | 49 | # enter the main dijkstra loop 50 | for iter = 1:nverts 51 | if iter == 1 52 | root = u 53 | else 54 | root = mod(u+iter-1,nverts) + 1 55 | if L[root] > 0 56 | continue 57 | end 58 | end 59 | n = 1 60 | T[n] = root 61 | L[root] = n # oops, n is now the size of the heap 62 | d[root] = 0 63 | while n > 0 64 | v = T[1] 65 | L[v] = -1 66 | ntop = T[n] 67 | T[1] = ntop 68 | n = n-1 69 | if n > 0 70 | L[ntop] = 1 71 | end # pop the head off the heap 72 | k = 1 73 | kt = ntop # move element T[1] down the heap 74 | while true 75 | i = 2*k 76 | if i > n 77 | break 78 | end # end of heap 79 | if i == n 80 | it = T[i] # only one child, so skip 81 | else # pick the smallest child 82 | lc = T[i] 83 | rc = T[i+1] 84 | it = lc 85 | if d[rc] < d[lc] 86 | i = i+1 87 | it = rc 88 | end # right child is smaller 89 | end 90 | if d[kt] < d[it] 91 | break # at correct place, so end 92 | else 93 | T[k] = it 94 | L[it] = k 95 | T[i] = kt 96 | L[kt] = i 97 | k=i # swap 98 | end 99 | end # end heap down 100 | 101 | # for each vertex adjacent to v, relax it 102 | for ei = rp[v]:rp[v+1]-1 # ei is the edge index 103 | w = ci[ei] 104 | ew = ai[ei] # w is the target, ew is the edge weight 105 | if L[w] < 0 106 | continue 107 | end # make sure we don't visit w twice 108 | # relax edge (v,w,ew) 109 | if d[w] > ew 110 | d[w] = ew 111 | pred[w] = v 112 | # check if w is in the heap 113 | k = L[w] 114 | onlyup = false 115 | if k == 0 116 | # element not in heap, only move the element up the heap 117 | n = n + 1 118 | T[n] = w 119 | L[w] = n 120 | k = n 121 | kt = w 122 | onlyup = true 123 | else 124 | kt = T[k] 125 | end 126 | # update the heap, move the element down in the heap 127 | while !onlyup 128 | i = 2*k 129 | if i > n 130 | break 131 | end # end of heap 132 | if i == n 133 | it = T[i] # only one child, so skip 134 | else # pick the smallest child 135 | lc = T[i] 136 | rc = T[i+1] 137 | it = lc 138 | if d[rc] < d[lc] 139 | i = i+1 140 | it = rc 141 | end # right child is smaller 142 | end 143 | if d[kt] < d[it] 144 | break # at correct place, so end 145 | else 146 | T[k] = it 147 | L[it] = k 148 | T[i] = kt 149 | L[kt] = i 150 | k = i # swap 151 | end 152 | end 153 | # move the element up the heap 154 | j = k 155 | tj = T[j] 156 | while j > 1 # j==1 => element at top of heap 157 | j2 = convert(Int64,floor(j/2)) 158 | tj2 = T[j2] # parent element 159 | if d[tj2] < d[tj] 160 | break # parent is smaller, so done 161 | else # parent is larger, so swap 162 | T[j2] = tj 163 | L[tj] = j2 164 | T[j] = tj2 165 | L[tj2] = j 166 | j = j2 167 | end 168 | end 169 | end 170 | end 171 | end 172 | if !full 173 | break 174 | end 175 | end 176 | nmstedges = 0 177 | for i = 1:nverts 178 | if pred[i] > 0 179 | nmstedges = nmstedges + 1 180 | end 181 | end 182 | ti = zeros(Int64,nmstedges) 183 | tj = copy(ti) 184 | tv = zeros(Int64,nmstedges) 185 | k = 1 186 | for i = 1:nverts 187 | if pred[i] > 0 188 | j = pred[i] 189 | ti[k] = i 190 | tj[k] = j 191 | for rpi = rp[i]:rp[i+1]-1 192 | if ci[rpi] == j 193 | tv[k] = ai[rpi] 194 | break 195 | end 196 | end 197 | k = k + 1 198 | end 199 | end 200 | 201 | return (ti,tj,tv,nverts) 202 | 203 | end 204 | 205 | ### Support more input: 206 | mst_prim(A::MatrixNetwork) = mst_prim(A,false,1) 207 | mst_prim(A::MatrixNetwork,full::Bool) = mst_prim(A,full,1) 208 | 209 | ### Support sparse matrices: 210 | 211 | mst_prim(A::SparseMatrixCSC{T,Int64},full::Bool,u::Int64) where {T} = mst_prim(MatrixNetwork(A),full,u) 212 | mst_prim(A::SparseMatrixCSC{T,Int64},full::Bool) where {T} = mst_prim(MatrixNetwork(A),full,1) 213 | mst_prim(A::SparseMatrixCSC{T,Int64}) where {T} = mst_prim(MatrixNetwork(A),false,1) 214 | 215 | 216 | ### Output modifiers: 217 | function mst_prim_matrix(A::MatrixNetwork,full::Bool,u::Int64) 218 | (ti,tj,tv,nverts) = mst_prim(A,full,u) 219 | T = sparse(ti,tj,tv,nverts,nverts) 220 | T = T + T' 221 | return T 222 | end 223 | mst_prim_matrix(A::MatrixNetwork) = mst_prim_matrix(A,false,1) 224 | mst_prim_matrix(A::MatrixNetwork,full::Bool) = mst_prim_matrix(A,full,1) 225 | 226 | mst_prim_matrix(A::SparseMatrixCSC{T,Int64},full::Bool,u::Int64) where {T} = mst_prim_matrix(MatrixNetwork(A),full,u) 227 | mst_prim_matrix(A::SparseMatrixCSC{T,Int64},full::Bool) where {T} = mst_prim_matrix(MatrixNetwork(A),full,1) 228 | mst_prim_matrix(A::SparseMatrixCSC{T,Int64}) where {T} = mst_prim_matrix(MatrixNetwork(A),false,1) 229 | -------------------------------------------------------------------------------- /data/airports.labels: -------------------------------------------------------------------------------- 1 | "Abbotsford, BC" 2 | "Aberdeen, SD" 3 | "Abilene, TX" 4 | "Akron/Canton, OH" 5 | "Alamosa, CO" 6 | "Albany, GA" 7 | "Albany, NY" 8 | "Albuquerque, NM" 9 | "Alexandria, LA" 10 | "Allentown/Bethlehem/Easton, PA" 11 | "Alliance, NE" 12 | "Alpena, MI" 13 | "Altoona, PA" 14 | "Amarillo, TX" 15 | "Anchorage, AK" 16 | "Appleton, WI" 17 | "Asheville, NC" 18 | "Aspen, CO" 19 | "Athens, GA" 20 | "Atlanta, GA" 21 | "Atlantic City, NJ" 22 | "Augusta, GA" 23 | "Augusta, ME" 24 | "Austin, TX" 25 | "Bagotville, QC" 26 | "Baie Comeau, QC" 27 | "Bakersfield, CA" 28 | "Baltimore, MD" 29 | "Bangor, ME" 30 | "Bar Harbor, ME" 31 | "Barrow, AK" 32 | "Bathurst, NB" 33 | "Baton Rouge, LA" 34 | "Beaumont/Pt. Arthur, TX" 35 | "Beckley, WV" 36 | "Bellingham, WA" 37 | "Bemidji, MN" 38 | "Bethel, AK" 39 | "Billings, MT" 40 | "Binghamton, NY" 41 | "Birmingham, AL" 42 | "Bismarck, ND" 43 | "Blanc Sablon, QC" 44 | "Bloomington, IL" 45 | "Bluefield, WV" 46 | "Boise, ID" 47 | "Boston, MA" 48 | "Bozeman, MT" 49 | "Bradford, PA" 50 | "Brainerd, MN" 51 | "Brownsville, TX" 52 | "Brunswick, GA" 53 | "Buffalo, NY" 54 | "Burbank, CA" 55 | "Burlington, IA" 56 | "Burlington, VT" 57 | "Butte, MT" 58 | "Calgary, AB" 59 | "Campbell River, BC" 60 | "Cape Girardeau, MO" 61 | "Casper, WY" 62 | "Castlegar, BC" 63 | "Cedar City, UT" 64 | "Cedar Rapids/Iowa City, IA" 65 | "Chadron, NE" 66 | "Champaign, IL" 67 | "Charleston, SC" 68 | "Charleston, WV" 69 | "Charlotte, NC" 70 | "Charlottesville, VA" 71 | "Charlottetown, PE" 72 | "Chattanooga, TN" 73 | "Cheyenne, WY" 74 | "Chibougamau, QC" 75 | "Chicago, IL" 76 | "Chico, CA" 77 | "Cincinnati, OH" 78 | "Clarksburg, WV" 79 | "Cleveland, OH" 80 | "Clovis, NM" 81 | "Cody, WY" 82 | "College Station, TX" 83 | "Colorado Springs, CO" 84 | "Columbia, MO" 85 | "Columbia, SC" 86 | "Columbus, GA" 87 | "Columbus, OH" 88 | "Columbus/Starkville/West Point, MS" 89 | "Comox, BC" 90 | "Cordova, AK" 91 | "Corpus Christi, TX" 92 | "Cortez, CO" 93 | "Cranbrook, BC" 94 | "Crescent City, CA" 95 | "Dallas/Fort Worth, TX" 96 | "Dayton, OH" 97 | "Daytona Beach, FL" 98 | "Decatur, IL" 99 | "Deer Lake, NL" 100 | "Del Rio, TX" 101 | "Denver, CO" 102 | "Des Moines, IA" 103 | "Detroit, MI" 104 | "Devils Lake, ND" 105 | "Dickinson, ND" 106 | "Dillingham, AK" 107 | "Dodge City, KS" 108 | "Dothan, AL" 109 | "Dubois, PA" 110 | "Dubuque, IA" 111 | "Duluth/Superior, MN" 112 | "Durango, CO" 113 | "Eau Claire, WI" 114 | "Edmonton, AB" 115 | "El Centro/Imperial, CA" 116 | "El Paso, TX" 117 | "Elko, NV" 118 | "Elmira/Corning, NY" 119 | "Enid, OK" 120 | "Erie, PA" 121 | "Escanaba, MI" 122 | "Eugene, OR" 123 | "Eureka/Arcata, CA" 124 | "Evansville, IN" 125 | "Fairbanks, AK" 126 | "Fargo, ND" 127 | "Farmington, NM" 128 | "Fayetteville, AR" 129 | "Fayetteville, NC" 130 | "Flint, MI" 131 | "Florence, SC" 132 | "Franklin, PA" 133 | "Fredericton, NB" 134 | "Fresno, CA" 135 | "Ft. Dodge, IA" 136 | "Ft. Huachuca/Sierra Vista, AZ" 137 | "Ft. Lauderdale, FL" 138 | "Ft. Leonard Wood, MO" 139 | "Ft. McMurray, AB" 140 | "Ft. Myers, FL" 141 | "Ft. Smith, AR" 142 | "Ft. St John, BC" 143 | "Ft. Walton Beach, FL" 144 | "Ft. Wayne, IN" 145 | "Gainesville, FL" 146 | "Garden City, KS" 147 | "Gaspe, QC" 148 | "Gillette, WY" 149 | "Goose Bay, NL" 150 | "Grand Canyon, AZ" 151 | "Grand Forks, ND" 152 | "Grand Island, NE" 153 | "Grand Junction, CO" 154 | "Grand Rapids, MI" 155 | "Grande Prairie, AB" 156 | "Great Bend, KS" 157 | "Great Falls, MT" 158 | "Green Bay, WI" 159 | "Greenbrier, WV" 160 | "Greensboro/High Point/Winston Salem, NC" 161 | "Greenville, MS" 162 | "Greenville, NC" 163 | "Greenville/Spartanburg, SC" 164 | "Gulfport/Biloxi, MS" 165 | "Gunnison, CO" 166 | "Hagerstown, MD" 167 | "Halifax, NS" 168 | "Hancock, MI" 169 | "Harlingen, TX" 170 | "Harrisburg, PA" 171 | "Hartford, CT" 172 | "Hays, KS" 173 | "Helena, MT" 174 | "Hibbing/Chisholm, MN" 175 | "Hilo, HI" 176 | "Hilton Head Island, SC" 177 | "Honolulu, HI" 178 | "Hoolehua, HI" 179 | "Houston, TX" 180 | "Huntington, WV" 181 | "Huntsville/Decatur, AL" 182 | "Hyannis, MA" 183 | "Idaho Falls, ID" 184 | "Iles de la Madeleine, QC" 185 | "Indianapolis, IN" 186 | "International Falls, MN" 187 | "Inyokern, CA" 188 | "Iron Mountain, MI" 189 | "Ironwood, MI" 190 | "Ithaca, NY" 191 | "Jackson, MS" 192 | "Jackson, TN" 193 | "Jackson, WY" 194 | "Jacksonville, FL" 195 | "Jacksonville, NC" 196 | "Jamestown, ND" 197 | "Jamestown, NY" 198 | "Johnstown, PA" 199 | "Joplin, MO" 200 | "Juneau, AK" 201 | "Kahului, HI" 202 | "Kalamazoo, MI" 203 | "Kalispell, MT" 204 | "Kamloops, BC" 205 | "Kansas City, MO" 206 | "Kapalua, HI" 207 | "Kearney, NE" 208 | "Kelowna, BC" 209 | "Kenai, AK" 210 | "Ketchikan, AK" 211 | "Key West, FL" 212 | "Killeen, TX" 213 | "Kingston, ON" 214 | "Kinston, NC" 215 | "Kirksville, MO" 216 | "Kitchener/Waterloo, ON" 217 | "Klamath Falls, OR" 218 | "Knoxville, TN" 219 | "Kodiak, AK" 220 | "Kona, HI" 221 | "Kotzebue, AK" 222 | "La Crosse, WI" 223 | "Lafayette, LA" 224 | "Lake Charles, LA" 225 | "Lake Havasu City, AZ" 226 | "Lanai City, HI" 227 | "Lancaster, PA" 228 | "Lansing, MI" 229 | "Laramie, WY" 230 | "Laredo, TX" 231 | "Las Vegas, NV" 232 | "Latrobe, PA" 233 | "Laurel/Hattiesburg, MS" 234 | "Lawton, OK" 235 | "Lebanon, NH" 236 | "Lethbridge, AB" 237 | "Lewiston, ID" 238 | "Lexington, KY" 239 | "Liberal, KS" 240 | "Lihue, HI" 241 | "Lincoln, NE" 242 | "Little Rock, AR" 243 | "London, ON" 244 | "Long Beach, CA" 245 | "Long Island MacArthur, NY" 246 | "Longview, TX" 247 | "Los Angeles, CA" 248 | "Louisville, KY" 249 | "Lubbock, TX" 250 | "Lynchburg, VA" 251 | "Macon, GA" 252 | "Madison, WI" 253 | "Manchester, NH" 254 | "Manhattan, KS" 255 | "Manistee, MI" 256 | "Marion, IL" 257 | "Marquette, MI" 258 | "Mason City, IA" 259 | "Massena, NY" 260 | "McAllen, TX" 261 | "McCook, NE" 262 | "Medford, OR" 263 | "Medicine Hat, AB" 264 | "Melbourne, FL" 265 | "Memphis, TN" 266 | "Meridian, MS" 267 | "Miami, FL" 268 | "Midland/Odessa, TX" 269 | "Milwaukee, WI" 270 | "Minneapolis/St Paul, MN" 271 | "Minot, ND" 272 | "Missoula, MT" 273 | "Mobile, AL" 274 | "Modesto, CA" 275 | "Moline, IL" 276 | "Moncton, NB" 277 | "Monroe, LA" 278 | "Mont Joli, QC" 279 | "Monterey, CA" 280 | "Montgomery, AL" 281 | "Montreal, QC" 282 | "Montrose, CO" 283 | "Morgantown, WV" 284 | "Moses Lake, WA" 285 | "Muscle Shoals, AL" 286 | "Muskegon, MI" 287 | "Myrtle Beach, SC" 288 | "Nanaimo, BC" 289 | "Nantucket, MA" 290 | "Naples, FL" 291 | "Nashville, TN" 292 | "New Bern, NC" 293 | "New Haven, CT" 294 | "New Orleans, LA" 295 | "New York, NY" 296 | "Newburgh, NY" 297 | "Newport News, VA" 298 | "Nome, AK" 299 | "Norfolk/Virginia Beach/Williamsburg, VA" 300 | "North Bay, ON" 301 | "North Bend, OR" 302 | "North Platte, NE" 303 | "Oakland, CA" 304 | "Ogdensburg, NY" 305 | "Oklahoma City, OK" 306 | "Omaha, NE" 307 | "Ontario, CA" 308 | "Orange County, CA" 309 | "Orlando, FL" 310 | "Ottawa, ON" 311 | "Owensboro, KY" 312 | "Oxnard/Ventura, CA" 313 | "Paducah, KY" 314 | "Palm Springs, CA" 315 | "Panama City, FL" 316 | "Parkersburg/Marietta, WV" 317 | "Pasco, WA" 318 | "Pellston, MI" 319 | "Pendleton, OR" 320 | "Pensacola, FL" 321 | "Penticton, BC" 322 | "Peoria, IL" 323 | "Petersburg, AK" 324 | "Philadelphia, PA" 325 | "Phoenix, AZ" 326 | "Pierre, SD" 327 | "Pittsburgh, PA" 328 | "Plattsburgh, NY" 329 | "Pocatello, ID" 330 | "Ponca City, OK" 331 | "Portland, ME" 332 | "Portland, OR" 333 | "Prescott, AZ" 334 | "Presque Isle, ME" 335 | "Prince George, BC" 336 | "Prince Rupert, BC" 337 | "Providence, RI" 338 | "Pueblo, CO" 339 | "Pullman, WA" 340 | "Quebec, QC" 341 | "Quesnel, BC" 342 | "Quincy, IL" 343 | "Raleigh/Durham, NC" 344 | "Rankin Inlet, NU" 345 | "Rapid City, SD" 346 | "Redding, CA" 347 | "Redmond, OR" 348 | "Regina, SK" 349 | "Reno, NV" 350 | "Rhinelander, WI" 351 | "Richmond, VA" 352 | "Riverton, WY" 353 | "Roanoke, VA" 354 | "Roberval, QC" 355 | "Rochester, MN" 356 | "Rochester, NY" 357 | "Rock Springs, WY" 358 | "Rockland, ME" 359 | "Rouyn-Noranda, QC" 360 | "Rutland, VT" 361 | "Sacramento, CA" 362 | "Saginaw, MI" 363 | "Salina, KS" 364 | "Salisbury-Ocean City, MD" 365 | "Salt Lake City, UT" 366 | "San Angelo, TX" 367 | "San Antonio, TX" 368 | "San Diego, CA" 369 | "San Francisco, CA" 370 | "San Jose, CA" 371 | "San Luis Obispo, CA" 372 | "Sandspit, BC" 373 | "Santa Barbara, CA" 374 | "Santa Fe, NM" 375 | "Santa Maria, CA" 376 | "Saranac Lake, NY" 377 | "Sarasota/Bradenton, FL" 378 | "Sarnia, ON" 379 | "Saskatoon, SK" 380 | "Sault Ste Marie, MI" 381 | "Sault Ste Marie, ON" 382 | "Savannah, GA" 383 | "Scottsbluff, NE" 384 | "Seattle/Tacoma, WA" 385 | "Sept-Iles, QC" 386 | "Shenandoah Valley, VA" 387 | "Sheridan, WY" 388 | "Show Low, AZ" 389 | "Shreveport, LA" 390 | "Sioux City, IA" 391 | "Sioux Falls, SD" 392 | "Sitka, AK" 393 | "Smithers, BC" 394 | "South Bend, IN" 395 | "Spokane, WA" 396 | "Springfield, IL" 397 | "Springfield, MO" 398 | "St. Anthony, NL" 399 | "St. Cloud, MN" 400 | "St. George, UT" 401 | "St. John, NB" 402 | "St. Johns, NL" 403 | "St. Louis, MO" 404 | "State College, PA" 405 | "Steamboat Springs, CO" 406 | "Sudbury, ON" 407 | "Syracuse, NY" 408 | "Tallahassee, FL" 409 | "Tampa/St. Petersburg, FL" 410 | "Telluride, CO" 411 | "Terrace, BC" 412 | "Texarkana, AR" 413 | "Thief River Falls, MN" 414 | "Thunder Bay, ON" 415 | "Timmins, ON" 416 | "Toledo, OH" 417 | "Toronto, ON" 418 | "Traverse City, MI" 419 | "Tri-City Airport, TN" 420 | "Tucson, AZ" 421 | "Tulsa, OK" 422 | "Tupelo, MS" 423 | "Twin Falls, ID" 424 | "Tyler, TX" 425 | "Vail, CO" 426 | "Val D'Or, QC" 427 | "Valdez, AK" 428 | "Valdosta, GA" 429 | "Vancouver, BC" 430 | "Victoria, BC" 431 | "Victoria, TX" 432 | "Wabush, NL" 433 | "Waco, TX" 434 | "Walla Walla, WA" 435 | "Washington, DC" 436 | "Waterloo, IA" 437 | "Watertown, NY" 438 | "Watertown, SD" 439 | "Wausau, WI" 440 | "Wenatchee, WA" 441 | "West Palm Beach, FL" 442 | "Westchester County, NY" 443 | "Whitehorse, YT" 444 | "Wichita Falls, TX" 445 | "Wichita, KS" 446 | "Wilkes-Barre/Scranton, PA" 447 | "Williams Lake, BC" 448 | "Williamsport, PA" 449 | "Williston, ND" 450 | "Wilmington, NC" 451 | "Windsor, ON" 452 | "Winnipeg, MB" 453 | "Worland, WY" 454 | "Yakima, WA" 455 | "Yellowknife, NT" 456 | "Yuma, AZ" -------------------------------------------------------------------------------- /data/geoclusters-example.xy: -------------------------------------------------------------------------------- 1 | .9962974170599902 1.2063017264551246 2 | 1.0238124286995574 1.1414844193099394 3 | 1.1830695347087843 1.0622631751265117 4 | 1.085184203333971 1.2327429137856907 5 | .9274672641804946 1.0342577226769223 6 | .9733628096010624 .9770491992981841 7 | .9725941847010301 1.096310931950135 8 | 1.0037814009003985 .9528104491582853 9 | 1.1487379819185684 1.0783997189518715 10 | .9143167262382002 1.0558803202421028 11 | 1.1130474420942296 .9350330702826525 12 | .9928736700001345 1.1300434323812412 13 | .9940765632273852 1.0384754158519458 14 | .9755440017199647 1.0127770395474271 15 | 1.0481970646612344 1.2479366954909676 16 | .9808867491952674 2.0818174378786014 17 | 1.0249952942951226 2.052459692018489 18 | 1.0831194496949132 2.0453395917083723 19 | .9691695247833424 1.9228029417212973 20 | .9776308004433298 2.134032900401744 21 | 1.0712232340268282 1.92979002743032 22 | 1.035854537886146 1.972229108372604 23 | 1.090968251582497 2.1685658718655016 24 | 1.0600966181341958 2.1604872535599826 25 | 1.085290024468346 1.9927417734687518 26 | 1.0121187850934261 2.0167838690414075 27 | .9959111315285641 1.7819702215619146 28 | .98554129226492 2.119538843094487 29 | .9657420431761412 1.9453530079877392 30 | .7310184702728733 2.123886957374385 31 | 1.0760085352541726 2.987167323301099 32 | .9696144147552838 3.063120108891489 33 | 1.0085406089832094 3.0687996647838505 34 | .9498674460400458 2.9504697295326796 35 | 1.0613311572066746 3.1080871793209925 36 | .9756695054942338 2.8718604151411506 37 | .9593243257176255 2.9826960124609236 38 | .8971519901830367 2.794854390631259 39 | 1.096059418890589 3.0953066557869073 40 | 1.185906496272323 2.984004200110597 41 | .922030702912715 2.8920976274702235 42 | 1.0111860461271542 3.0079736095838463 43 | 1.1250054566693908 2.865389253551674 44 | .7990395771394115 2.9296003010579175 45 | .929002233299023 2.943256103897544 46 | 1.0317026723132654 4.102026876776336 47 | .9251693407314016 3.8822716020721435 48 | .9072441422733939 4.012054771715842 49 | 1.085123001991168 3.895683586771444 50 | 1.1673851218607119 4.012254692966482 51 | .947656428657291 3.9898677850902016 52 | 1.0143330078493893 3.976054610136695 53 | .9815130474251257 4.271579453506326 54 | 1.0989765467043233 4.0054781324735895 55 | .8613931405236455 4.0605618220577675 56 | 1.2451548498025382 4.0038974659772135 57 | 1.030149333237188 4.000132631758023 58 | .95142814223014 3.990278805199195 59 | 1.1003390345576753 4.010392611038708 60 | 1.1157407247643536 3.9522362894486327 61 | 1.9706793678310917 1.1255002614611787 62 | 2.0566241091396136 .9726921131066836 63 | 2.2012808593550917 .9908919276379855 64 | 1.8600010168807242 1.337734134298482 65 | 1.9975611073543886 1.2039160614785471 66 | 2.061451539276203 1.0559865394526937 67 | 1.8461042609493519 .8970570284691192 68 | 2.0415682119841185 1.0543539665766426 69 | 1.9946075654920334 1.1910255505726364 70 | 1.9781041075129722 1.0935735736492025 71 | 2.032653255669123 .9083140634508262 72 | 1.9673149264527099 .9964517360260934 73 | 2.1040176259276415 .8484106294541938 74 | 2.0746172706745947 1.1143722513364342 75 | 1.9583238819627007 .9760236952156803 76 | 2.116366771079058 1.9940384534725466 77 | 2.0811764310173553 2.106467534864788 78 | 1.9953446655693206 1.9963504464171113 79 | 2.0825040757547106 1.9015919652598172 80 | 1.89612044447715 2.1369017815529845 81 | 1.9550276725192144 1.9111660404380322 82 | 1.883277085010705 1.9042553312957295 83 | 2.038910780212237 2.016796390926376 84 | 2.076645603519108 2.042232906505808 85 | 2.1945539184417204 1.7696536104607694 86 | 1.974238072281341 1.8129790505522851 87 | 1.7688248707856458 2.0251049908892647 88 | 1.9393077485022379 2.1258286399710262 89 | 1.8091719822933394 1.9576530675046364 90 | 1.9226480235264927 2.058856712065475 91 | 2.00266294383173 2.9678402801461643 92 | 1.907559414815598 2.8840557094264883 93 | 1.9738726757927403 3.006970260201061 94 | 2.124607954028228 2.8454518288407873 95 | 1.9442402085542334 3.0639755568793086 96 | 2.110626996624313 3.04289072473676 97 | 1.8033623664882201 2.9844452616439696 98 | 1.8466764295354412 2.976536107355189 99 | 2.0889176810928802 2.9550565169673897 100 | 1.9776184761201787 2.8172978606811263 101 | 2.132455449349608 2.9631619052392417 102 | 1.8008324781150342 3.102986327770356 103 | 2.0402317153669265 2.9358319521313487 104 | 1.8389924028532338 2.8970543834547935 105 | 2.073077877069835 3.024784253751789 106 | 1.9149280544051595 4.0076749597076065 107 | 2.064788284971803 3.877260594940499 108 | 1.9666541338080241 4.193117856175961 109 | 2.0123567365318746 3.993055928796424 110 | 1.9881740640062506 3.934295510998587 111 | 1.9775383925853545 4.1086445335010335 112 | 1.8951553857632641 4.0708424502355065 113 | 1.9801041123894563 4.026470782908428 114 | 2.008327266619982 3.9635992162258757 115 | 2.099912803855045 3.952405340760959 116 | 2.1237281602585987 3.8968043062737694 117 | 1.8986911286760058 3.899825728142259 118 | 1.9810567577823581 4.019455868154423 119 | 2.0134863995248975 3.876560743986197 120 | 2.0344518310819697 4.04176289726062 121 | 2.9180357197608893 .9308691537035579 122 | 2.8507917373974725 .8356895668316019 123 | 3.1241425105931313 .9898512258744864 124 | 3.0569668422375194 .9861488565975738 125 | 3.021437987513484 1.0389341283975646 126 | 2.918265438736579 .9078333128756146 127 | 2.900077927008528 1.1762798785940685 128 | 2.9681704646220757 1.1104792074910659 129 | 3.061596891082538 1.00374780446305 130 | 3.0282087602036847 1.0121100774957505 131 | 3.01264192530423 1.0419144737084676 132 | 2.8205174440894574 1.017607523485581 133 | 2.8691926065573083 1.0804018192690885 134 | 2.770035069284223 1.0804052039522571 135 | 2.8545032389235234 1.10077753265089 136 | 3.0838138936139474 2.155757444944803 137 | 2.9482743214195777 2.00311259670886 138 | 3.0803288782689493 2.0241432727913504 139 | 2.948155108789622 2.161566085784853 140 | 2.8886819194580147 2.214976659032436 141 | 2.933116992700797 2.056774797803017 142 | 3.062205730372081 1.9512512841923981 143 | 2.9602952804581903 2.054114046501972 144 | 3.0365286473872968 2.26815516868207 145 | 3.216729714852914 1.9603174129179868 146 | 3.1281889513757535 1.8881154937310562 147 | 2.9545289297121875 1.912153255082257 148 | 2.9288549573848126 1.8294891119117445 149 | 3.070305189451719 1.8851162586037526 150 | 2.9010871707493955 1.9813142940688775 151 | 2.932549444971435 2.877977278092157 152 | 3.2021893296459676 3.149570502756159 153 | 2.8085644928239484 3.0520310951277856 154 | 3.047408062244185 3.117199009463059 155 | 3.1977945901301483 2.8395779532739436 156 | 2.9535417582932975 3.014586162617204 157 | 3.003750762249712 3.1197879747055075 158 | 3.128074667320826 2.9359965527899194 159 | 3.0109724984201804 2.9248372125369553 160 | 3.033752415512672 3.1330959453194285 161 | 3.0335350885368335 2.937583635885985 162 | 3.0038072011205945 2.978422604777724 163 | 2.9827074973736156 3.1305486097675748 164 | 2.783971179941295 2.929583974626884 165 | 3.0397120100331816 2.886414472702264 166 | 3.0064735058426297 3.9853543375755596 167 | 3.0295046227809173 3.931595338629587 168 | 2.9421968079873935 3.967590220775169 169 | 2.894366760258411 3.931245092276139 170 | 2.975778104772558 3.981189169705278 171 | 2.9529929152003223 3.9647947569640083 172 | 2.978714412242855 4.079614845735874 173 | 2.9257209482964814 3.9207785406407827 174 | 3.1307517598371764 3.940500852598997 175 | 2.933108321312753 4.02069119617914 176 | 2.972865057102714 3.968189064353931 177 | 2.8527897231597152 4.065905793562491 178 | 2.932096471999298 3.9968187559485924 179 | 2.8704165510658064 4.185554922054489 180 | 2.9595880448997676 3.9217826426539864 181 | 4.134529881017234 .6838397238681293 182 | 3.9424246518697457 .8616343077645667 183 | 3.85637803414761 1.0120712204463638 184 | 3.9757168595519237 1.0722756128431665 185 | 4.042389058866297 1.0132940635109735 186 | 4.004899634645531 1.1498578805134325 187 | 3.8223556856562078 .8813537370607267 188 | 4.096514361847407 .9367384637892278 189 | 4.052657301068084 .888710939901678 190 | 4.113768652452052 1.0263603995149198 191 | 3.9948896379963155 1.0378175999490944 192 | 4.0313235408572465 1.0221782311207892 193 | 3.9407512369066704 .9212748778693466 194 | 4.065653268592624 1.0079897722774296 195 | 3.8467424853002674 1.0898680108547498 196 | 4.049959205281174 1.9710154103938138 197 | 3.9788102941231536 2.0909167773057753 198 | 3.924696992417776 1.83056075871226 199 | 3.8473880631631197 2.0372593424568213 200 | 3.981203294261552 1.8996092744914763 201 | 3.8247940476545326 2.0429585171890485 202 | 4.0940213826556136 1.9135203486683225 203 | 3.9612029241203572 1.9702444673899888 204 | 3.9400233296345712 2.1330308614769433 205 | 3.984519466664694 1.9714902784298745 206 | 4.165229090779098 1.9820547299799156 207 | 3.9341831942092464 2.063146263908742 208 | 4.134372061016702 1.9025010607765034 209 | 3.8349447587057397 2.0237392128487306 210 | 3.887274849220268 2.217614474558693 211 | 3.9999366393392704 2.9718618499771945 212 | 4.038718200981796 3.1929498865392487 213 | 4.044204546682514 2.981673117969324 214 | 3.84183913816842 2.8902419389550666 215 | 4.023592565843811 2.9038656230938695 216 | 4.1644229426387485 2.944949553740312 217 | 3.8768530316146737 2.9975766396443206 218 | 3.9947897033013877 2.965766672950769 219 | 3.864122143197984 2.951629851432776 220 | 4.100755175430194 3.1367366050773073 221 | 3.9117685601544934 2.7579647712427495 222 | 4.132055612027885 3.1424277998306316 223 | 4.0255887858707045 2.983041431792641 224 | 4.0778706360709895 2.992497605434197 225 | 3.931135231851984 2.9766171544055275 226 | 3.9217344138275005 3.9272643494383472 227 | 3.9122798042151596 3.9656846237972783 228 | 3.86224774947172 3.952103030203158 229 | 4.100715397773366 3.823155705857074 230 | 3.835983744134015 4.108716327862356 231 | 3.9723544819514665 3.963291941714004 232 | 4.071480018459701 3.902243822630165 233 | 3.9999630347097295 3.919275344958119 234 | 3.9266738591234978 4.0341353052835 235 | 3.9911727911493484 3.9442929275834935 236 | 3.926653559435045 3.9792298466163394 237 | 4.075689997265636 3.966887766894342 238 | 4.080684351696394 3.832068844727393 239 | 3.978301571705213 3.922236871634044 240 | 4.091909253423529 3.9669723414025677 241 | -------------------------------------------------------------------------------- /test/diffusions_test.jl: -------------------------------------------------------------------------------- 1 | import KahanSummation 2 | using LinearAlgebra 3 | using SparseArrays 4 | 5 | using Test 6 | @testset "diffusions" begin 7 | function _normout(P::SparseArrays.SparseMatrixCSC{T,Int64}) where T 8 | n = size(P,1) 9 | colsums = sum(P,dims=1) 10 | (pi,pj,pv) = findnz(P) 11 | P = sparse(pi,pj,pv./colsums[pj],n,n) 12 | end 13 | 14 | function _normout(P::LinearAlgebra.Adjoint{Float64,SparseArrays.SparseMatrixCSC{T,Int64}}) where T 15 | n = size(P,1) 16 | colsums = sum(P,dims=1) 17 | pj,pi,pv = findnz(P.parent) 18 | P = sparse(pi,pj,pv./colsums[pj],n,n) 19 | end 20 | 21 | function prlinsys(A::SparseMatrixCSC{Float64,Int64},alpha::Float64,v::Vector{Float64}) 22 | P = Matrix(_normout(A')) 23 | n = size(A,1) 24 | z = (Matrix(1.0I,n,n) - alpha*P) \ v 25 | z = z /sum(z) 26 | return z 27 | end 28 | 29 | @testset "pagerank" begin 30 | 31 | A = load_matrix_network("celegans") 32 | x = pagerank(A, 0.85) 33 | y = pagerank(A, 0.85, 1e-2) 34 | 35 | n = 10 36 | A = sparse(1.0I,10,10) 37 | v = rand(n) 38 | vn = v/sum(v) 39 | x = personalized_pagerank(A,0.85,v) 40 | @test norm(x-vn,1) <= n*eps(Float64) 41 | 42 | v = 5; 43 | x = personalized_pagerank(A,0.85,v) 44 | xtrue = zeros(n) 45 | xtrue[5] = 1. 46 | @test norm(x -xtrue,1) <= n*eps(Float64) 47 | 48 | x = seeded_pagerank(A,0.85,Set([5])) 49 | 50 | x = personalized_pagerank(A,0.85,Set([5])) 51 | 52 | 53 | @test norm(x -xtrue,1) <= n*eps(Float64) 54 | 55 | 56 | x = personalized_pagerank(A,0.85,Dict{Int64,Float64}(5 => 6.)) 57 | 58 | @test norm(x -xtrue,1) <= n*eps(Float64) 59 | 60 | 61 | x = personalized_pagerank(A,0.85,Set(collect(1:8))) 62 | 63 | @test norm(x - sparsevec(collect(1:8),1.0/8,n),1) <= n*eps(Float64) 64 | 65 | 66 | x = personalized_pagerank(A,0.85,Dict{Int64,Float64}(zip(1:8, ones(8)))) 67 | @test norm(x - sparsevec(collect(1:8),1.0/8,n),1) <= n*eps(Float64) 68 | 69 | @test_throws ArgumentError personalized_pagerank(A,0.85,sparsevec(Dict{Int64,Float64}(zip(1:8, ones(8))))) 70 | 71 | x = personalized_pagerank(A,0.85,sparsevec(Dict{Int64,Float64}(zip(4:10, ones(7))))) 72 | @test norm(x - sparsevec(collect(4:10),1.0/7,n),1) <= n*eps(Float64) 73 | 74 | x = personalized_pagerank(A, 0.85, sparsevec([5], [2.], 10)) 75 | @test norm(x -xtrue,1) <= n*eps(Float64) 76 | 77 | A = sparse(1I,n,n) 78 | x = personalized_pagerank(A,0.85,5) 79 | @test norm(x -xtrue,1) <= n*eps(Float64) 80 | 81 | n = 25 82 | A = LinearAlgebra.fillstored!(copy(sprand(n,n,2/n)), 1) 83 | x = pagerank(A,0.85) 84 | xtrue = prlinsys(A,0.85,ones(n)) 85 | @test norm(x -xtrue,1) <= n*eps(Float64) 86 | 87 | x = pagerank(MatrixNetwork(A),0.85) 88 | @test norm(x -xtrue,1) <= n*eps(Float64) 89 | 90 | n = 10 91 | A = spdiagm(1=>ones(n-1)) 92 | v = 1.0/n 93 | tol = 1e-8 94 | x = pagerank(A,0.85) 95 | z = zeros(n) 96 | z[1] = 1. 97 | z[2] = 1.85 98 | for ii=3:n 99 | z[ii] = z[ii-1] + 0.85*(z[ii-1]-z[ii-2]) 100 | end 101 | z = z/KahanSummation.sum_kbn(z) 102 | @test norm(x-z,1) <= n*tol 103 | 104 | end 105 | 106 | @testset "pagerank_error" begin 107 | @test_throws DimensionMismatch pagerank(spzeros(6,5),0.85) 108 | 109 | @test_throws ArgumentError pagerank(spzeros(5,5),-0.85) 110 | @test_throws ArgumentError pagerank(spzeros(5,5),1.0) 111 | @test_throws ArgumentError pagerank(spzeros(5,5),1.01) 112 | 113 | @test_throws ArgumentError personalized_pagerank(spzeros(5,5), 0.85, rand(11)) 114 | @test_throws DomainError personalized_pagerank(spzeros(5,5), 0.85, -ones(5)) 115 | @test_throws DomainError personalized_pagerank(spzeros(5,5), 0.85, -sparsevec(ones(5))) 116 | end 117 | 118 | 119 | @testset "pagerank_alg" begin 120 | n = 10 121 | P = sparse(1.0I,n,n) 122 | 123 | x = zeros(n) 124 | y = zeros(n) 125 | v = rand(n) 126 | v = v/sum(v) 127 | tol = 1e-8 128 | maxiter = 1000 129 | iterfunc = MatrixNetworks._noiterfunc 130 | 131 | x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,iterfunc) 132 | @test norm(x-v,1) <= n*tol 133 | 134 | # P = spdiagm(ones(n-1),-1,n,n) 135 | P = spdiagm(-1=>ones(n-1)) 136 | v .= 0.0 137 | v[1] = 1. 138 | x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,iterfunc) 139 | z = zeros(n) 140 | z[1] = 1. 141 | 142 | for ii=2:n 143 | z[ii] = 0.85*z[ii-1] 144 | end 145 | z = z/KahanSummation.sum_kbn(z) 146 | @test norm(x-z,1) <= n*tol 147 | 148 | tol = 1e-12 149 | x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,iterfunc) 150 | @test norm(x-z,1) <= n*tol 151 | 152 | tol = 1e-12 153 | x = pagerank_power!(x,y,P,0.85,sparsevec(v),tol,maxiter,iterfunc) 154 | @test norm(x-z,1) <= n*tol 155 | 156 | 157 | tol = 1e-15 158 | x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,iterfunc) 159 | @test norm(x-z,1) <= n*tol 160 | 161 | tol = 1e-3 162 | x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,(iter,x) -> @show iter, norm(x,1)) 163 | @test norm(x-z,1) <= n*tol 164 | 165 | v = 1.0/n 166 | tol = 1e-8 167 | x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,iterfunc) 168 | z[1] = 1. 169 | z[2] = 1.85 170 | 171 | for ii=3:n 172 | z[ii] = z[ii-1] + 0.85*(z[ii-1]-z[ii-2]) 173 | end 174 | z = z/KahanSummation.sum_kbn(z) 175 | 176 | @test norm(x-z,1) <= n*tol 177 | 178 | end 179 | 180 | @testset "pagerank_perf" begin 181 | maxiter=500 182 | 183 | n = 1_000 184 | x = zeros(n) 185 | y = zeros(n) 186 | z = zeros(n) 187 | tol = 1e-8 188 | v = 1.0/n 189 | A = LinearAlgebra.fillstored!(copy(sprand(n,n,25/n)),1) 190 | 191 | P = _normout(A') 192 | 193 | x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 194 | P2 = MatrixNetworks._create_stochastic_mult(A) 195 | y = pagerank_power!(y,z,P2,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 196 | P3 = MatrixNetworks._create_stochastic_mult(MatrixNetwork(A)) 197 | y = pagerank_power!(y,z,P3,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 198 | 199 | n = 10_000 200 | x = zeros(n) 201 | y = zeros(n) 202 | z = zeros(n) 203 | v = 1.0/n 204 | tol = 1e-8 205 | A = LinearAlgebra.fillstored!(copy(sprand(n,n,25/n)), 1) 206 | P = _normout(A') 207 | 208 | dt = @elapsed x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 209 | dt = @elapsed x = pagerank_power!(x,y,P,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 210 | P2 = MatrixNetworks._create_stochastic_mult(A) 211 | dt2 = @elapsed y = pagerank_power!(y,z,P2,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 212 | dt2 = @elapsed y = pagerank_power!(y,z,P2,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 213 | 214 | #@test dt2 <= 2*dt 215 | @test norm(x-y) <= n*eps(Float64) 216 | 217 | # now test with matrix networks 218 | P3 = MatrixNetworks._create_stochastic_mult(MatrixNetwork(A)) 219 | 220 | y = zeros(n) 221 | z = zeros(n) 222 | dt2 = @elapsed y = pagerank_power!(y,z,P3,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 223 | dt2 = @elapsed y = pagerank_power!(y,z,P3,0.85,v,tol,maxiter,MatrixNetworks._noiterfunc) 224 | @test norm(x-y) <= n*eps(Float64) 225 | #@test dt2 <= 2*dt 226 | 227 | dt = @elapsed x = pagerank_power!(x,y,P,0.85,v,eps(Float64),1000,MatrixNetworks._noiterfunc) 228 | dt2 = @elapsed y = pagerank(A,0.85) 229 | @test norm(x-y) <= n*eps(Float64) 230 | @test dt2 <= 2*dt 231 | end 232 | 233 | function shkexpm(A,t,v) 234 | P = Matrix(_normout(A')) 235 | return exp(-t*(Matrix(1.0I,size(A,1),size(A,1))-P))*v 236 | end 237 | 238 | @testset "heatkernel" begin 239 | 240 | seeded_stochastic_heat_kernel(sparse(1.0I,5,5),2.,1) 241 | seeded_stochastic_heat_kernel(sparse(1.0I,5,5),2.,Set([1])) 242 | seeded_stochastic_heat_kernel(sparse(1.0I,5,5),2.,Dict{Int,Float64}(1 => 1.)) 243 | seeded_stochastic_heat_kernel(sparse(1.0I,5,5),2.,sparsevec([1],[1.],5)) 244 | seeded_stochastic_heat_kernel(sparse(1.0I,5,5),2.,sparse([1],[1],[1.], 5, 1)) 245 | v = zeros(5) 246 | v[1] = 1. 247 | seeded_stochastic_heat_kernel(sparse(1.0I,5,5),2.,v) 248 | seeded_stochastic_heat_kernel(sparse(1.0I,5,5),2.,1.0/5.0) 249 | 250 | n = 5 251 | v = rand(5) 252 | vn = v/sum(v) 253 | x = seeded_stochastic_heat_kernel(sparse(1.0I,5,5),2.,vn) 254 | @test norm(x-vn,1) <= n*eps(Float64) 255 | 256 | n = 25 257 | A = LinearAlgebra.fillstored!(copy(sprand(n,n,2/n)), 1) 258 | 259 | v = ones(n)./n 260 | x = seeded_stochastic_heat_kernel(A,2.,v) 261 | xtrue = shkexpm(A,2.,v) 262 | @test norm(x -xtrue,1) <= n*eps(Float64) 263 | 264 | x = seeded_stochastic_heat_kernel(MatrixNetwork(A),2.,v) 265 | @test norm(x -xtrue,1) <= n*eps(Float64) 266 | 267 | n = 10 268 | # A = spdiagm(ones(n-1),-1,n,n)' 269 | A = copy(spdiagm(-1=>ones(n-1))') 270 | v = zeros(n) 271 | v[1] = 1. 272 | t = 2. 273 | x = seeded_stochastic_heat_kernel(A,t,v) 274 | z = zeros(n) 275 | z[1] = exp(-t) 276 | for ii=2:n 277 | z[ii] = z[ii-1]*t./(ii-1.) 278 | end 279 | @test norm(x-z,1) <= n*eps(Float64) 280 | 281 | @test abs(seeded_stochastic_heat_kernel(spzeros(1,1),2.,1)[1] - exp(-2.)) <= 10*eps(Float64) 282 | @test abs(seeded_stochastic_heat_kernel(spzeros(1,1),5.,1)[1] - exp(-5.)) <= 10*eps(Float64) 283 | @test abs(seeded_stochastic_heat_kernel(sparse(1.0I,1,1),2.,1)[1] - 1.) <= 10*eps(Float64) 284 | end 285 | @testset "StochasticMult" begin 286 | n = 10 287 | 288 | A = MatrixNetwork(LinearAlgebra.fillstored!(copy(sprand(n,n,2/n)), 1)) 289 | 290 | MNSM = MatrixNetworks.MatrixNetworkStochasticMult(rand(n), A) 291 | @test size(MNSM) == (n,n) 292 | @test size(MNSM, 1) == n 293 | @test length(MNSM) == n*n 294 | @test eltype(MNSM) == Float64 295 | @test ndims(MNSM) == 2 296 | 297 | A = LinearAlgebra.fillstored!(copy(sprand(n,n,2/n)), 1) 298 | SMSM = MatrixNetworks.SparseMatrixStochasticMult(rand(n), A) 299 | @test size(SMSM) == (n,n) 300 | @test size(SMSM, 1) == n 301 | @test length(SMSM) == n*n 302 | @test eltype(SMSM) == Float64 303 | @test ndims(SMSM) == 2 304 | end 305 | end 306 | -------------------------------------------------------------------------------- /src/bipartite_matching.jl: -------------------------------------------------------------------------------- 1 | #TODO: more detailed documentation? 2 | """ 3 | BIPARTITE MATCHING 4 | ------------------ 5 | return a maximum weight bipartite matching of a graph 6 | 7 | Functions 8 | --------- 9 | - Matching_Setup = bipartite_matching_setup{T}(A::SparseMatrixCSC{T,Int64}) 10 | - Matching_Setup = bipartite_matching_setup{T}(x::Vector{T},ei::Vector{Int64},ej::Vector{Int64},m::Int64,n::Int64) 11 | - Matching_Output = bipartite_matching_primal_dual{T}(rp::Vector{Int64}, ci::Vector{Int64},ai::Vector{T}, m::Int64, n::Int64) 12 | - Matching_Output = bipartite_matching{T}(A::SparseMatrixCSC{T,Int64}) 13 | - Matching_Output = bipartite_matching{T}(w::Vector{T},ei::Vector{Int64},ej::Vector{Int64},m::Int64,n::Int64) 14 | - Matching_Output = bipartite_matching{T}(w::Vector{T},ei::Vector{Int64},ej::Vector{Int64}) 15 | - ind = bipartite_matching_indicator{T}(w::Vector{T},ei::Vector{Int64},ej::Vector{Int64}) 16 | - (m1,m2) = edge_list(M_output::Matching_output) 17 | - S = create_sparse(M_output::Matching_output)\n 18 | You can check the documentation of each of the output modifiers functions separately. 19 | 20 | Example 21 | ------- 22 | ~~~ 23 | W = sprand(10,8,0.5) 24 | bipartite_matching(W) 25 | ei = [1;2;3] 26 | ej = [3;2;4] 27 | Matching_Output = bipartite_matching([10;12;13],ei,ej) 28 | Matching_Output.weight 29 | Matching_Output.cardinality 30 | Matching_Output.match 31 | S = create_sparse(bipartite_matching(W)) # get the sparse matrix 32 | (m1,m2) = edge_list(bipartite_matching(W)) # get the edgelist 33 | ~~~ 34 | """ 35 | function bipartite_matching end 36 | 37 | mutable struct Matching_setup 38 | rp::Array{Int64,1} 39 | ci::Array{Int64,1} 40 | ai::Array{Float64,1} 41 | tripi::Array{Int64,1} 42 | m::Int64 43 | n::Int64 44 | end 45 | 46 | mutable struct Matching_output 47 | m::Int64 48 | n::Int64 49 | weight::Float64 50 | cardinality::Int64 51 | match::Array{Int64,1} 52 | end 53 | 54 | ###################### 55 | # setup funtions # 56 | ###################### 57 | 58 | function bipartite_matching_setup_phase1(A::SparseMatrixCSC{T,Int64}) where T 59 | (nzi,nzj,nzv) = findnz(A) 60 | return (nzi,nzj,nzv) 61 | end 62 | 63 | function bipartite_matching_setup(A::SparseMatrixCSC{T,Int64}) where T 64 | (nzi,nzj,nzv) = bipartite_matching_setup_phase1(A) 65 | nedges = length(nzi) 66 | (m,n)=size(A) 67 | 68 | rp = ones(Int64,m+1) # csr matrix with extra edges 69 | ci = zeros(Int64,nedges+m) 70 | ai = zeros(Float64,nedges+m) 71 | 72 | 73 | rp[1]=0 74 | for i=1:nedges 75 | rp[nzi[i]+1]=rp[nzi[i]+1]+1 76 | end 77 | rp=cumsum(rp) 78 | 79 | for i=1:nedges 80 | ai[rp[nzi[i]]+1]=nzv[i] 81 | ci[rp[nzi[i]]+1]=nzj[i] 82 | rp[nzi[i]]=rp[nzi[i]]+1 83 | end 84 | 85 | for i=1:m # add the extra edges 86 | ai[rp[i]+1]=0 87 | ci[rp[i]+1]=n+i 88 | rp[i]=rp[i]+1 89 | end 90 | 91 | # restore the row pointer array 92 | for i=m:-1:1 93 | rp[i+1]=rp[i] 94 | end 95 | rp[1]=0 96 | rp=rp.+1 97 | 98 | #check for duplicates in the data 99 | colind = zeros(Int64,m+n) 100 | for i=1:m 101 | for rpi=rp[i]:rp[i+1]-1 102 | if colind[ci[rpi]] == 1 103 | error("bipartite_matching:duplicateEdge") 104 | end 105 | colind[ci[rpi]]=1 106 | end 107 | 108 | for rpi=rp[i]:rp[i+1]-1 109 | colind[ci[rpi]]=0 110 | end # reset indicator 111 | end 112 | 113 | M_setup = Matching_setup(rp,ci,ai,[],m,n) 114 | return M_setup 115 | end 116 | 117 | 118 | function bipartite_matching_setup(x::Vector{T},ei::Vector{Int64}, 119 | ej::Vector{Int64},m::Int64,n::Int64) where T 120 | (nzi,nzj,nzv) = (ei,ej,x) 121 | nedges = length(nzi) 122 | 123 | rp = ones(Int64,m+1) # csr matrix with extra edges 124 | ci = zeros(Int64,nedges+m) 125 | ai = zeros(Float64,nedges+m) 126 | tripi = zeros(Int64,nedges+m) 127 | # 1. build csr representation with a set of extra edges from vertex i to 128 | # vertex m+i 129 | 130 | rp[1]=0 131 | for i=1:nedges 132 | rp[nzi[i]+1]=rp[nzi[i]+1]+1 133 | end 134 | 135 | rp=cumsum(rp) 136 | for i=1:nedges 137 | tripi[rp[nzi[i]]+1]=i 138 | ai[rp[nzi[i]]+1]=nzv[i] 139 | ci[rp[nzi[i]]+1]=nzj[i] 140 | rp[nzi[i]]=rp[nzi[i]]+1 141 | end 142 | for i=1:m # add the extra edges 143 | tripi[rp[i]+1]=-1 144 | ai[rp[i]+1]=0 145 | ci[rp[i]+1]=n+i 146 | rp[i]=rp[i]+1 147 | end 148 | 149 | # restore the row pointer array 150 | for i=m:-1:1 151 | rp[i+1]=rp[i] 152 | end 153 | 154 | rp[1]=0 155 | rp=rp.+1 156 | 157 | #check for duplicates in the data 158 | colind = zeros(Int64,m+n) 159 | 160 | for i=1:m 161 | for rpi=rp[i]:rp[i+1]-1 162 | if colind[ci[rpi]] == 1 163 | error("bipartite_matching:duplicateEdge") 164 | end 165 | colind[ci[rpi]]=1 166 | end 167 | 168 | for rpi=rp[i]:rp[i+1]-1 169 | colind[ci[rpi]]=0 170 | end # reset indicator 171 | end 172 | M_setup = Matching_setup(rp,ci,ai,tripi,m,n) 173 | return M_setup 174 | end 175 | 176 | ################## 177 | # primal-dual # 178 | ################## 179 | 180 | function bipartite_matching_primal_dual(rp::Vector{Int64}, ci::Vector{Int64}, 181 | ai::Vector{T}, m::Int64, n::Int64) where T 182 | 183 | # variables used for the primal-dual algorithm 184 | # normalize ai values # updated on 2-19-2019 185 | ai ./= maximum(abs.(ai)) 186 | alpha=zeros(Float64,m) 187 | bt=zeros(Float64,m+n)#beta 188 | queue=zeros(Int64,m) 189 | t=zeros(Int64,m+n) 190 | match1=zeros(Int64,m) 191 | match2=zeros(Int64,m+n) 192 | tmod = zeros(Int64,m+n) 193 | ntmod=0 194 | 195 | # initialize the primal and dual variables 196 | for i=1:m 197 | for rpi=rp[i]:rp[i+1]-1 198 | if ai[rpi] > alpha[i] 199 | alpha[i]=ai[rpi] 200 | end 201 | end 202 | end 203 | 204 | # dual variables (bt) are initialized to 0 already 205 | # match1 and match2 are both 0, which indicates no matches 206 | 207 | i=1 208 | while i<=m 209 | for j=1:ntmod 210 | t[tmod[j]]=0 211 | end 212 | ntmod=0 213 | # add i to the stack 214 | head=1 215 | tail=1 216 | queue[head]=i 217 | while head <= tail && match1[i]==0 218 | k=queue[head] 219 | for rpi=rp[k]:rp[k+1]-1 220 | j = ci[rpi] 221 | if ai[rpi] < alpha[k] + bt[j] - 1e-8 222 | continue 223 | end # skip if tight 224 | if t[j]==0 225 | tail=tail+1 226 | if tail <= m 227 | queue[tail]=match2[j] 228 | end 229 | t[j]=k 230 | ntmod=ntmod+1 231 | tmod[ntmod]=j 232 | if match2[j]<1 233 | while j>0 234 | match2[j]=t[j] 235 | k=t[j] 236 | temp=match1[k] 237 | match1[k]=j 238 | j=temp 239 | end 240 | break 241 | end 242 | end 243 | end 244 | head=head+1 245 | end 246 | if match1[i] < 1 247 | theta=Inf 248 | for j=1:head-1 249 | t1=queue[j] 250 | for rpi=rp[t1]:rp[t1+1]-1 251 | t2=ci[rpi] 252 | if t[t2] == 0 && alpha[t1] + bt[t2] - ai[rpi] < theta 253 | theta = alpha[t1] + bt[t2] - ai[rpi] 254 | end 255 | end 256 | end 257 | for j=1:head-1 258 | alpha[queue[j]] -= theta 259 | end 260 | for j=1:ntmod 261 | bt[tmod[j]] += theta 262 | end 263 | continue 264 | end 265 | i=i+1 266 | end 267 | val=0 268 | for i=1:m 269 | for rpi=rp[i]:rp[i+1]-1 270 | if ci[rpi]==match1[i] 271 | val=val+ai[rpi] 272 | end 273 | end 274 | end 275 | noute = 0 276 | for i=1:m 277 | if match1[i]<=n 278 | noute=noute+1 279 | end 280 | end 281 | 282 | M_output = Matching_output(m,n,val,noute,match1) 283 | return M_output 284 | end 285 | 286 | function bipartite_matching_primal_dual(M_setup::Matching_setup) 287 | return bipartite_matching_primal_dual(M_setup.rp, M_setup.ci, M_setup.ai, 288 | M_setup.m, M_setup.n) 289 | end 290 | 291 | #################### 292 | ## Functions # 293 | #################### 294 | 295 | function bipartite_matching(A::SparseMatrixCSC{T,Int64}) where T 296 | return bipartite_matching_primal_dual(bipartite_matching_setup(A)) 297 | end 298 | 299 | 300 | function bipartite_matching(w::Vector{T},ei::Vector{Int64}, 301 | ej::Vector{Int64},m::Int64,n::Int64) where T 302 | return bipartite_matching_primal_dual(bipartite_matching_setup(w,ei,ej,m,n)) 303 | end 304 | 305 | function bipartite_matching(w::Vector{T},ei::Vector{Int64}, 306 | ej::Vector{Int64}) where T 307 | return bipartite_matching_primal_dual(bipartite_matching_setup( 308 | w,ei,ej,maximum(ei),maximum(ej))) 309 | end 310 | 311 | #################### 312 | ## Indicator # 313 | #################### 314 | 315 | """ 316 | Returns the matching indicator of a matrix stored in triplet format 317 | Example: 318 | bipartite_matching_indicator([10;12;13],[1;2;3],[3;2;4]) 319 | """ 320 | function bipartite_matching_indicator(w::Vector{T},ei::Vector{Int64}, 321 | ej::Vector{Int64}) where T 322 | M_setup = bipartite_matching_setup(w, ei, ei, maximum(ei), maximum(ej)) 323 | M_out = bipartite_matching_primal_dual(M_setup.rp, M_setup.ci, M_setup.ai, 324 | M_setup.m, M_setup.n) 325 | return edge_indicator(M_out,ei, ej) 326 | end 327 | 328 | 329 | ######################### 330 | # Output Modifiers # 331 | ######################### 332 | """ 333 | Returns the edge list of a matching output 334 | Example: 335 | M_out = bipartite_matching([10;12;13],[1;2;3],[3;2;4]) 336 | edge_list(M_out) 337 | """ 338 | function edge_list(M_output::Matching_output) 339 | m1=zeros(Int64,M_output.cardinality) 340 | m2=zeros(Int64,M_output.cardinality) 341 | noute=1 342 | for i=1:M_output.m 343 | if M_output.match[i]<=M_output.n 344 | m1[noute]=i 345 | m2[noute]=M_output.match[i] 346 | noute=noute+1 347 | end 348 | end 349 | return (m1,m2) 350 | end 351 | 352 | ########## 353 | """ 354 | Creates and returns a sparse matrix that represents the outputed matching 355 | Example: 356 | M_out = bipartite_matching([10;12;13],[1;2;3],[3;2;4]) 357 | create_sparse(M_out) 358 | """ 359 | function create_sparse(M_output::Matching_output) 360 | (ei,ej) = edge_list(M_output) 361 | A = sparse(ei,ej,1,M_output.m,M_output.n) 362 | return A 363 | end 364 | 365 | ########## 366 | function edge_indicator(M_output::Matching_output, ei::Vector, ej::Vector) 367 | @assert length(ei) == length(ej) 368 | ind = zeros(Int64,length(ei)) 369 | for i=1:length(ei) 370 | if M_output.match[ei[i]] == ej[i] 371 | ind[i] = 1 372 | end 373 | end 374 | return ind 375 | end 376 | -------------------------------------------------------------------------------- /src/triangles.jl: -------------------------------------------------------------------------------- 1 | # tested with symmetries = false 2 | struct tri_struct 3 | v1::Int64 4 | v2::Int64 5 | v3::Int64 6 | end 7 | 8 | struct triangles_iterator 9 | tri::tri_struct 10 | triinfo::tri_struct 11 | symmetries::Bool 12 | S::Union{UnitRange{Int64},Vector{Int64},Int} 13 | rp::Vector{Int} 14 | ci::Vector{Int} 15 | vindicator::Vector{Bool} 16 | windicator::Vector{Bool}#only needed for the unique case 17 | end 18 | 19 | function next_triangle_unique!(tri::tri_struct,triinfo::tri_struct, 20 | S::Union{UnitRange{Int64},Vector{Int64},Int}, 21 | rp::Vector{Int}, 22 | ci::Vector{Int}, 23 | vindicator::Vector{Bool}, 24 | windicator::Vector{Bool}) 25 | v,w,x = (tri.v1,tri.v2,tri.v3) 26 | vi,wi,xi = (triinfo.v1,triinfo.v2,triinfo.v3) 27 | # start with checking x 28 | wrng = rp[v]:rp[v+1]-1 29 | xrng = rp[w]:rp[w+1]-1 30 | # v size(A) 105 | (10, 10) 106 | 107 | julia> @btime collect(triangles(A;symmetries=true)) 108 | 33.490 μs (18 allocations: 53.23 KiB) 109 | 110 | old: 111 | julia> @btime collect(triangles(A;symmetries=true)) 112 | 51.747 μs (18 allocations: 53.23 KiB) 113 | =# 114 | function next_triangle!(tri::tri_struct,triinfo::tri_struct, 115 | S::Union{UnitRange{Int64},Vector{Int64},Int}, 116 | rp::Vector{Int}, 117 | ci::Vector{Int}, 118 | vindicator::Vector{Bool}, 119 | windicator::Vector{Bool}, 120 | step_tri::Int) 121 | if step_tri <= 4 122 | return _iterate_on_current_triangle(tri,step_tri),triinfo 123 | else 124 | return next_triangle_unique!(tri_struct(tri.v3,tri.v2,tri.v1),triinfo,S,rp,ci,vindicator,windicator) 125 | end 126 | end 127 | 128 | function triprint(t::tri_struct) 129 | @show t.v1,t.v2,t.v3 130 | end 131 | 132 | function _iterate_on_current_triangle(cur_tri::tri_struct,step::Int) 133 | # only 5 cases, enumerate 134 | if step == 0 135 | return tri_struct(cur_tri.v1,cur_tri.v3,cur_tri.v2) 136 | elseif step == 1 137 | return tri_struct(cur_tri.v3,cur_tri.v1,cur_tri.v2) 138 | elseif step == 2 139 | return tri_struct(cur_tri.v1,cur_tri.v3,cur_tri.v2) 140 | elseif step == 3 141 | return tri_struct(cur_tri.v2,cur_tri.v3,cur_tri.v1) 142 | elseif step == 4 143 | return tri_struct(cur_tri.v1,cur_tri.v3,cur_tri.v2) 144 | else 145 | error("This error should not be reached. Step must be 0 <= step <= 4") 146 | end 147 | end 148 | function Base.iterate(iter::triangles_iterator, state=(iter.tri, iter.triinfo,0)) 149 | element, elinfo, step_tri = state 150 | 151 | if element == tri_struct(0, 0, 0) 152 | update_vindicator!(iter.vindicator,iter.S[1],iter.rp,iter.ci) 153 | iter.windicator.=0 154 | return nothing 155 | end 156 | 157 | if iter.symmetries 158 | v1,v2 = next_triangle!(element,elinfo,iter.S,iter.rp,iter.ci,iter.vindicator,iter.windicator,step_tri) 159 | step_tri += 1 160 | step_tri %= 6 161 | else 162 | v1,v2 = next_triangle_unique!(element,elinfo,iter.S,iter.rp,iter.ci,iter.vindicator,iter.windicator) 163 | end 164 | return (element, (v1,v2,step_tri)) 165 | end 166 | 167 | function find_first_triangle(S::Union{UnitRange{Int64},Vector{Int64},Int}, 168 | rp::Vector{Int}, 169 | ci::Vector{Int}, 170 | n::Int) 171 | vi = 1 172 | v = S[vi] 173 | rng = rp[v]:rp[v+1]-1 174 | while !(length(rng)>0) && vi0) 184 | return tri_struct(0,0,0),tri_struct(0,0,0),vindicator,windicator # this graph has no edges! 185 | else 186 | @inbounds for rpi = rp[v]:rp[v+1]-1 187 | w = ci[rpi] 188 | vindicator[w] = true 189 | end 190 | wi = 1 191 | w = ci[rng[wi]] 192 | vec1,vec2 = next_triangle_unique!(tri_struct(v,w,0),tri_struct(vi,wi,0),S,rp,ci,vindicator,windicator) 193 | return vec1,vec2,vindicator,windicator 194 | end 195 | end 196 | 197 | 198 | function update_vindicator!(vindicator::Vector{Bool},v::Int,rp::Vector{Int},ci::Vector{Int}) 199 | vindicator .= false 200 | @inbounds for rpi = rp[v]:rp[v+1]-1 201 | w = ci[rpi] 202 | vindicator[w] = true 203 | end 204 | end 205 | 206 | # old function: (uses the matrix A) 207 | # function triangles(A::SparseMatrixCSC{T,Int64},S::Union{UnitRange{Int64},Vector{Int64},Int};symmetries=false) where T 208 | # M = MatrixNetwork(A) 209 | 210 | # if is_undirected(M) == false 211 | # error("Only undirected (symmetric) inputs are allowed") 212 | # end 213 | 214 | # rp,ci,ai = (M.rp,M.ci,M.vals) 215 | # if typeof(findfirst(ai.<0)) != Nothing 216 | # error("only positive edge weights allowed") 217 | # end 218 | 219 | # v1,v2 = find_first_triangle(S,rp,ci,A) 220 | # first_triangle = v1 #onetriangle(v1[1],v1[2],v1[3]) 221 | # first_triangle_info = v2 #onetriangle_info(v2[1],v2[2],v2[3]) 222 | # my_tri_iter = triangles_iterator(first_triangle,first_triangle_info,symmetries,S,rp,ci,A) 223 | # end 224 | 225 | """ 226 | - triangles(A) return an iterator for all the triangles in a graph A 227 | - triangles(A,i) return an iterator for all triangles in a graph A involving node i 228 | - triangles(A,S) return an iterator for all triangles in a graph A involving a node in S 229 | - triangles(A,S;symmetries=true) return an iterator of all triangles in A involving a node in S with symmetries 230 | (i.e. a triangle (i,j,k) is counted 6 times) 231 | sample run: 232 | ----------- 233 | ``` 234 | A = load_matrix_network("clique-10"); 235 | mytriangles = triangles(A) 236 | for tri in mytriangles 237 | MatrixNetworks.triprint(tri) 238 | end 239 | z = collect(mytriangles) 240 | ei,ej,ek = unzip_triangles(z) 241 | ``` 242 | A quick example to access the first triangle: 243 | ------- 244 | ``` 245 | julia> tri = first(mytriangles) 246 | MatrixNetworks.tri_struct(1, 2, 3) 247 | 248 | julia> tri.v1,tri.v2,tri.v3 249 | (1, 2, 3) 250 | ``` 251 | """ 252 | function triangles(M::MatrixNetwork{T},S::Union{UnitRange{Int64},Vector{Int64},Int};symmetries=false) where T 253 | 254 | if is_undirected(M) == false 255 | error("Only undirected (symmetric) inputs are allowed") 256 | end 257 | 258 | rp,ci,ai = (M.rp,M.ci,M.vals) 259 | if typeof(findfirst(ai.<0)) != Nothing 260 | error("only positive edge weights allowed") 261 | end 262 | 263 | v1,v2,vindicator,windicator = find_first_triangle(S,rp,ci,M.n) 264 | first_triangle = v1 #onetriangle(v1[1],v1[2],v1[3]) 265 | first_triangle_info = v2 #onetriangle_info(v2[1],v2[2],v2[3]) 266 | my_tri_iter = triangles_iterator(first_triangle,first_triangle_info,symmetries,S,rp,ci,vindicator,windicator) 267 | end 268 | function triangles(A::SparseMatrixCSC{T,Int64},S::Union{UnitRange{Int64},Vector{Int64},Int};symmetries=false) where T 269 | 270 | if maximum(S) > A.n 271 | error("triangles(A,S): the maximum value in S must be <= the number of nodes in the graph") 272 | end 273 | 274 | if issymmetric(A) == false 275 | error("Only undirected (symmetric) inputs are allowed") 276 | end 277 | 278 | rp,ci,ai = A.colptr,A.rowval,A.nzval 279 | if typeof(findfirst(ai.<0)) != Nothing 280 | error("Only positive edge weights allowed") 281 | end 282 | 283 | v1,v2,vindicator,windicator = find_first_triangle(S,rp,ci,A.n) 284 | first_triangle = v1 #onetriangle(v1[1],v1[2],v1[3]) 285 | first_triangle_info = v2 #onetriangle_info(v2[1],v2[2],v2[3]) 286 | my_tri_iter = triangles_iterator(first_triangle,first_triangle_info,symmetries,S,rp,ci,vindicator,windicator) 287 | end 288 | 289 | # generic functions 290 | triangles(G::SparseMatrixCSC{T,Int64};symmetries=false) where T = triangles(G,1:size(G,1);symmetries=symmetries) 291 | triangles(M::MatrixNetwork{T};symmetries=false) where T = triangles(M,1:M.n;symmetries=symmetries) 292 | # the following are no longer needed because of the general triangles function above 293 | # triangles(G::SparseMatrixCSC{T,Int64}) where T = triangles(G,1:size(G,1)) 294 | # triangles(G::SparseMatrixCSC{T,Int64},i::Int64) where T = triangles(G,i:i) 295 | # triangles(G::SparseMatrixCSC{T,Int64},i::Int64;symmetries=false) where T = triangles(G,i;symmetries=symmetries) 296 | 297 | import Base.collect 298 | function collect(tris::triangles_iterator) 299 | alltriangles = Array{Tuple{Int,Int,Int}}(undef,0) 300 | for ti in tris 301 | push!(alltriangles,(ti.v1,ti.v2,ti.v3)) 302 | end 303 | return alltriangles 304 | end 305 | 306 | function unzip_triangles(z::Vector{Tuple{Int64,Int64,Int64}}) 307 | n = length(z) 308 | ei = zeros(Int,n) 309 | ej = zeros(Int,n) 310 | ek = zeros(Int,n) 311 | @inbounds for i = 1:n 312 | ei[i],ej[i],ek[i] = z[i] 313 | end 314 | return ei,ej,ek 315 | end 316 | 317 | -------------------------------------------------------------------------------- /data/airports.xy: -------------------------------------------------------------------------------- 1 | -1.223288e+02 4.905157e+01 2 | -9.848732e+01 4.545909e+01 3 | -9.974142e+01 3.244917e+01 4 | -8.137157e+01 4.079781e+01 5 | -1.058736e+02 3.746818e+01 6 | -8.415599e+01 3.158076e+01 7 | -7.375527e+01 4.265145e+01 8 | -1.066486e+02 3.508418e+01 9 | -9.244565e+01 3.131268e+01 10 | -7.543422e+01 4.065143e+01 11 | -1.028715e+02 4.209712e+01 12 | -8.344515e+01 4.506157e+01 13 | -7.839890e+01 4.050719e+01 14 | -1.018339e+02 3.520726e+01 15 | -1.498577e+02 6.121756e+01 16 | -8.841656e+01 4.426178e+01 17 | -8.255314e+01 3.559846e+01 18 | -1.068182e+02 3.919003e+01 19 | -8.337326e+01 3.395813e+01 20 | -8.439111e+01 3.374831e+01 21 | -7.442652e+01 3.936281e+01 22 | -8.197531e+01 3.347909e+01 23 | -6.977622e+01 4.431804e+01 24 | -9.774298e+01 3.026760e+01 25 | -7.089266e+01 4.834590e+01 26 | -6.815248e+01 4.922104e+01 27 | -1.190188e+02 3.536697e+01 28 | -7.660960e+01 3.929055e+01 29 | -6.877078e+01 4.480172e+01 30 | -6.821146e+01 4.438882e+01 31 | -1.567888e+02 7.129008e+01 32 | -6.564889e+01 4.761679e+01 33 | -9.118687e+01 3.044342e+01 34 | -9.410158e+01 3.008615e+01 35 | -8.118765e+01 3.777759e+01 36 | -1.224712e+02 4.875235e+01 37 | -9.487965e+01 4.747315e+01 38 | -1.617586e+02 6.079026e+01 39 | -1.085058e+02 4.578397e+01 40 | -7.591127e+01 4.209869e+01 41 | -8.681150e+01 3.352029e+01 42 | -1.007793e+02 4.680537e+01 43 | -5.713521e+01 5.142862e+01 44 | -8.899771e+01 4.048501e+01 45 | -8.122263e+01 3.726799e+01 46 | -1.161934e+02 4.360698e+01 47 | -7.105670e+01 4.235863e+01 48 | -1.110332e+02 4.567932e+01 49 | -7.865048e+01 4.195586e+01 50 | -9.420069e+01 4.635316e+01 51 | -9.749553e+01 2.589958e+01 52 | -8.149556e+01 3.114976e+01 53 | -7.887846e+01 4.288544e+01 54 | -1.183081e+02 3.418147e+01 55 | -9.110078e+01 4.081200e+01 56 | -7.321323e+01 4.447592e+01 57 | -1.125209e+02 4.600175e+01 58 | -1.140632e+02 5.104507e+01 59 | -1.252482e+02 5.002461e+01 60 | -8.954453e+01 3.730271e+01 61 | -1.063277e+02 4.285010e+01 62 | -1.176542e+02 4.929626e+01 63 | -1.130618e+02 3.767795e+01 64 | -9.152653e+01 4.165782e+01 65 | -1.030002e+02 4.283175e+01 66 | -8.824350e+01 4.011420e+01 67 | -7.993160e+01 3.278115e+01 68 | -8.163893e+01 3.835021e+01 69 | -8.083754e+01 3.522250e+01 70 | -7.847752e+01 3.803213e+01 71 | -6.312817e+01 4.623659e+01 72 | -8.530942e+01 3.504643e+01 73 | -1.048215e+02 4.113482e+01 74 | -7.436878e+01 4.991365e+01 75 | -8.763241e+01 4.188415e+01 76 | -1.218420e+02 3.973201e+01 77 | -8.450469e+01 3.910644e+01 78 | -8.034012e+01 3.927942e+01 79 | -8.169046e+01 4.150436e+01 80 | -1.032053e+02 3.440523e+01 81 | -1.090572e+02 4.452608e+01 82 | -9.632590e+01 3.062054e+01 83 | -1.048217e+02 3.883311e+01 84 | -9.233416e+01 3.895217e+01 85 | -8.104525e+01 3.399855e+01 86 | -8.498763e+01 3.246292e+01 87 | -8.300301e+01 3.996200e+01 88 | -8.865042e+01 3.360651e+01 89 | -1.249268e+02 4.967354e+01 90 | -1.457546e+02 6.054062e+01 91 | -9.740356e+01 2.779635e+01 92 | -1.085537e+02 3.735466e+01 93 | -1.157687e+02 4.951350e+01 94 | -1.242005e+02 4.175281e+01 95 | -9.704090e+01 3.292222e+01 96 | -8.419444e+01 3.975905e+01 97 | -8.103940e+01 2.920358e+01 98 | -8.895246e+01 3.984584e+01 99 | -5.742366e+01 4.917534e+01 100 | -1.008982e+02 2.936624e+01 101 | -1.049923e+02 3.974001e+01 102 | -9.361567e+01 4.158978e+01 103 | -8.304792e+01 4.233169e+01 104 | -9.885730e+01 4.811227e+01 105 | -1.027906e+02 4.687901e+01 106 | -1.584656e+02 5.903978e+01 107 | -1.000187e+02 3.775264e+01 108 | -8.539330e+01 3.122324e+01 109 | -7.875975e+01 4.111978e+01 110 | -9.066327e+01 4.249913e+01 111 | -9.334919e+01 4.861401e+01 112 | -1.078793e+02 3.727367e+01 113 | -9.150124e+01 4.481651e+01 114 | -1.134904e+02 5.354622e+01 115 | -1.155695e+02 3.284748e+01 116 | -1.064875e+02 3.175916e+01 117 | -1.157676e+02 4.083563e+01 118 | -7.689729e+01 4.216331e+01 119 | -9.787834e+01 3.639708e+01 120 | -8.008524e+01 4.212944e+01 121 | -8.707976e+01 4.574522e+01 122 | -1.230889e+02 4.404991e+01 123 | -1.240852e+02 4.086743e+01 124 | -8.756413e+01 3.797691e+01 125 | -1.477221e+02 6.484520e+01 126 | -9.678176e+01 4.687591e+01 127 | -1.082061e+02 3.672814e+01 128 | -9.415791e+01 3.606321e+01 129 | -7.887758e+01 3.505450e+01 130 | -8.368602e+01 4.301075e+01 131 | -7.976906e+01 3.419363e+01 132 | -7.888378e+01 4.034320e+01 133 | -6.663912e+01 4.596064e+01 134 | -1.197857e+02 3.674061e+01 135 | -9.417762e+01 4.250157e+01 136 | -1.103462e+02 3.155479e+01 137 | -8.014356e+01 2.612367e+01 138 | -9.211671e+01 3.776971e+01 139 | -1.113803e+02 5.672718e+01 140 | -8.186819e+01 2.664084e+01 141 | -9.442130e+01 3.538460e+01 142 | -1.208475e+02 5.625247e+01 143 | -8.661467e+01 3.040422e+01 144 | -8.509906e+01 4.107552e+01 145 | -8.232318e+01 2.965195e+01 146 | -1.008732e+02 3.796918e+01 147 | -6.448067e+01 4.883145e+01 148 | -1.055048e+02 4.429319e+01 149 | -6.029594e+01 5.329582e+01 150 | -1.121221e+02 3.605488e+01 151 | -9.703203e+01 4.792408e+01 152 | -9.833990e+01 4.092474e+01 153 | -1.085645e+02 3.906879e+01 154 | -8.567118e+01 4.296641e+01 155 | -1.187949e+02 5.517116e+01 156 | -9.876515e+01 3.836601e+01 157 | -1.113061e+02 4.750715e+01 158 | -8.801016e+01 4.451279e+01 159 | -8.042690e+01 3.797558e+01 160 | -8.119489e+01 3.543548e+01 161 | -9.103786e+01 3.339847e+01 162 | -7.738022e+01 3.560719e+01 163 | -8.221670e+01 3.489067e+01 164 | -8.907202e+01 3.041334e+01 165 | -1.069269e+02 3.854495e+01 166 | -7.771758e+01 3.964250e+01 167 | -6.357392e+01 4.464616e+01 168 | -8.858567e+01 4.712660e+01 169 | -9.769555e+01 2.618986e+01 170 | -7.688222e+01 4.025988e+01 171 | -7.267407e+01 4.176333e+01 172 | -9.933224e+01 3.887094e+01 173 | -1.120212e+02 4.658976e+01 174 | -9.288378e+01 4.748975e+01 175 | -1.550819e+02 1.971924e+01 176 | -8.076224e+01 3.215482e+01 177 | -1.578576e+02 2.130474e+01 178 | -1.570764e+02 2.116935e+01 179 | -9.536978e+01 2.976045e+01 180 | -8.244537e+01 3.841983e+01 181 | -8.697976e+01 3.460738e+01 182 | -7.027963e+01 4.165479e+01 183 | -1.120420e+02 4.349200e+01 184 | -6.186639e+01 4.741877e+01 185 | -8.614996e+01 3.976691e+01 186 | -9.340365e+01 4.860260e+01 187 | -1.178130e+02 3.564694e+01 188 | -8.806525e+01 4.582075e+01 189 | -9.016423e+01 4.645554e+01 190 | -7.649545e+01 4.244049e+01 191 | -9.018049e+01 3.229869e+01 192 | -8.881940e+01 3.561390e+01 193 | -1.107624e+02 4.347996e+01 194 | -8.165580e+01 3.033138e+01 195 | -7.742105e+01 3.474929e+01 196 | -9.870853e+01 4.691009e+01 197 | -7.923780e+01 4.209605e+01 198 | -7.891835e+01 4.032726e+01 199 | -9.451317e+01 3.709146e+01 200 | -1.345795e+02 5.836773e+01 201 | -1.564671e+02 2.089078e+01 202 | -8.560111e+01 4.229243e+01 203 | -1.143149e+02 4.820164e+01 204 | -1.203379e+02 5.067612e+01 205 | -9.458306e+01 3.910296e+01 206 | -1.566636e+02 2.100012e+01 207 | -9.908369e+01 4.070047e+01 208 | -1.194966e+02 4.988681e+01 209 | -1.512598e+02 6.055312e+01 210 | -1.316478e+02 5.534208e+01 211 | -8.180063e+01 2.455426e+01 212 | -9.772711e+01 3.111683e+01 213 | -7.647933e+01 4.423154e+01 214 | -7.758180e+01 3.526869e+01 215 | -9.257199e+01 4.019426e+01 216 | -8.049305e+01 4.345123e+01 217 | -1.217821e+02 4.222389e+01 218 | -8.392091e+01 3.596050e+01 219 | -1.523952e+02 5.779435e+01 220 | -1.559957e+02 1.964014e+01 221 | -1.625859e+02 6.689724e+01 222 | -9.125192e+01 4.381262e+01 223 | -9.201705e+01 3.022032e+01 224 | -9.322011e+01 3.022403e+01 225 | -1.143459e+02 3.447398e+01 226 | -1.569196e+02 2.082796e+01 227 | -7.630127e+01 4.003804e+01 228 | -8.455228e+01 4.273202e+01 229 | -1.055903e+02 4.131081e+01 230 | -9.950201e+01 2.753092e+01 231 | -1.151400e+02 3.617191e+01 232 | -7.938494e+01 4.031665e+01 233 | -8.929039e+01 3.132323e+01 234 | -9.838983e+01 3.460515e+01 235 | -7.225395e+01 4.364481e+01 236 | -1.128337e+02 4.969383e+01 237 | -1.170167e+02 4.641866e+01 238 | -8.450032e+01 3.804859e+01 239 | -1.009252e+02 3.703239e+01 240 | -1.593686e+02 2.197492e+01 241 | -9.670773e+01 4.081361e+01 242 | -9.227449e+01 3.474865e+01 243 | -8.124621e+01 4.298689e+01 244 | -1.181924e+02 3.376642e+01 245 | -7.309839e+01 4.078913e+01 246 | -9.474107e+01 3.250064e+01 247 | -1.182450e+02 3.405329e+01 248 | -8.576640e+01 3.825486e+01 249 | -1.018559e+02 3.359233e+01 250 | -7.914284e+01 3.741402e+01 251 | -8.362758e+01 3.283969e+01 252 | -8.938666e+01 4.307292e+01 253 | -7.146309e+01 4.299117e+01 254 | -9.656183e+01 3.917881e+01 255 | -8.631898e+01 4.424172e+01 256 | -8.893005e+01 3.773082e+01 257 | -8.741368e+01 4.654584e+01 258 | -9.320104e+01 4.314665e+01 259 | -7.489284e+01 4.493135e+01 260 | -9.822936e+01 2.620742e+01 261 | -1.006253e+02 4.019860e+01 262 | -1.228756e+02 4.232356e+01 263 | -1.106781e+02 5.004107e+01 264 | -8.060688e+01 2.807939e+01 265 | -9.004891e+01 3.514966e+01 266 | -8.870307e+01 3.236503e+01 267 | -8.023742e+01 2.572899e+01 268 | -1.023619e+02 3.183238e+01 269 | -8.790684e+01 4.304181e+01 270 | -9.319871e+01 4.487927e+01 271 | -1.012961e+02 4.823611e+01 272 | -1.139962e+02 4.687278e+01 273 | -8.805324e+01 3.068640e+01 274 | -1.209996e+02 3.763830e+01 275 | -9.048990e+01 4.149173e+01 276 | -6.477826e+01 4.608837e+01 277 | -9.211905e+01 3.250965e+01 278 | -6.819107e+01 4.858794e+01 279 | -1.218967e+02 3.659758e+01 280 | -8.630063e+01 3.238012e+01 281 | -7.355439e+01 4.551229e+01 282 | -1.078776e+02 3.847865e+01 283 | -7.995785e+01 3.963038e+01 284 | -1.192772e+02 4.713194e+01 285 | -8.766771e+01 3.474244e+01 286 | -8.624593e+01 4.323424e+01 287 | -7.888268e+01 3.369357e+01 288 | -1.239362e+02 4.916427e+01 289 | -7.009933e+01 4.128522e+01 290 | -8.179579e+01 2.613909e+01 291 | -8.677837e+01 3.616776e+01 292 | -7.704207e+01 3.510843e+01 293 | -7.292496e+01 4.130712e+01 294 | -9.007771e+01 2.995369e+01 295 | -7.400712e+01 4.071455e+01 296 | -7.401023e+01 4.149994e+01 297 | -7.642945e+01 3.698051e+01 298 | -1.654047e+02 6.449956e+01 299 | -7.671206e+01 3.727566e+01 300 | -7.945993e+01 4.630663e+01 301 | -1.242245e+02 4.340661e+01 302 | -1.007633e+02 4.113632e+01 303 | -1.222730e+02 3.780506e+01 304 | -7.548960e+01 4.469385e+01 305 | -9.752033e+01 3.547201e+01 306 | -9.594047e+01 4.126064e+01 307 | -1.176478e+02 3.406457e+01 308 | -1.177651e+02 3.366666e+01 309 | -8.137739e+01 2.853774e+01 310 | -7.569189e+01 4.542150e+01 311 | -8.711354e+01 3.777420e+01 312 | -1.191804e+02 3.420037e+01 313 | -8.859583e+01 3.708571e+01 314 | -1.165121e+02 3.382321e+01 315 | -8.566033e+01 3.015987e+01 316 | -8.135612e+01 3.880775e+01 317 | -1.190946e+02 4.623518e+01 318 | -8.478448e+01 4.555174e+01 319 | -1.187864e+02 4.567179e+01 320 | -8.721724e+01 3.042084e+01 321 | -1.195939e+02 4.950023e+01 322 | -8.958776e+01 4.069214e+01 323 | -1.329709e+02 5.680650e+01 324 | -7.516237e+01 3.995227e+01 325 | -1.120758e+02 3.344826e+01 326 | -1.003502e+02 4.436892e+01 327 | -7.999746e+01 4.043831e+01 328 | -7.345303e+01 4.469913e+01 329 | -1.124506e+02 4.287472e+01 330 | -9.707923e+01 3.670360e+01 331 | -7.025665e+01 4.365915e+01 332 | -1.226756e+02 4.551179e+01 333 | -1.124702e+02 3.453901e+01 334 | -6.801538e+01 4.668160e+01 335 | -1.227451e+02 5.391235e+01 336 | -1.303262e+02 5.431304e+01 337 | -7.141199e+01 4.182388e+01 338 | -1.046124e+02 3.826386e+01 339 | -1.171815e+02 4.673016e+01 340 | -7.121936e+01 4.681275e+01 341 | -1.224982e+02 5.297812e+01 342 | -9.139583e+01 3.993560e+01 343 | -7.879234e+01 3.587289e+01 344 | -9.208335e+01 6.280714e+01 345 | -1.032309e+02 4.408116e+01 346 | -1.223929e+02 4.058752e+01 347 | -1.211759e+02 4.427257e+01 348 | -1.045952e+02 5.044801e+01 349 | -1.198116e+02 3.952455e+01 350 | -8.941131e+01 4.563839e+01 351 | -7.743341e+01 3.754055e+01 352 | -1.083813e+02 4.302467e+01 353 | -7.994053e+01 3.727152e+01 354 | -7.222446e+01 4.851526e+01 355 | -9.245978e+01 4.401925e+01 356 | -7.761603e+01 4.315550e+01 357 | -1.092228e+02 4.158616e+01 358 | -6.912908e+01 4.409757e+01 359 | -7.901451e+01 4.823707e+01 360 | -7.297156e+01 4.360487e+01 361 | -1.214910e+02 3.857906e+01 362 | -8.394667e+01 4.341918e+01 363 | -9.760970e+01 3.884054e+01 364 | -7.508438e+01 3.833501e+01 365 | -1.118882e+02 4.075952e+01 366 | -1.004424e+02 3.146150e+01 367 | -9.849462e+01 2.942449e+01 368 | -1.171617e+02 3.271569e+01 369 | -1.224200e+02 3.777916e+01 370 | -1.218858e+02 3.733848e+01 371 | -1.206626e+02 3.528552e+01 372 | -1.318239e+02 5.324659e+01 373 | -1.196988e+02 3.441929e+01 374 | -1.059374e+02 3.569154e+01 375 | -1.204350e+02 3.495313e+01 376 | -7.413178e+01 4.432453e+01 377 | -8.255163e+01 2.738679e+01 378 | -8.240336e+01 4.297877e+01 379 | -1.066593e+02 5.213050e+01 380 | -8.435172e+01 4.649231e+01 381 | -8.434790e+01 4.651828e+01 382 | -8.109072e+01 3.208078e+01 383 | -1.036628e+02 4.186180e+01 384 | -1.222961e+02 4.744048e+01 385 | -6.638823e+01 5.020864e+01 386 | -7.898762e+01 3.835303e+01 387 | -1.069559e+02 4.479769e+01 388 | -1.100428e+02 3.425030e+01 389 | -9.374727e+01 3.251461e+01 390 | -9.642084e+01 4.249979e+01 391 | -9.673128e+01 4.354535e+01 392 | -1.353351e+02 5.705372e+01 393 | -1.271681e+02 5.478280e+01 394 | -8.625404e+01 4.167906e+01 395 | -1.174123e+02 4.765726e+01 396 | -8.964360e+01 3.980105e+01 397 | -9.329156e+01 3.720898e+01 398 | -5.561335e+01 5.136656e+01 399 | -9.419752e+01 4.556586e+01 400 | -1.135833e+02 3.710907e+01 401 | -6.606412e+01 4.527343e+01 402 | -8.145206e+01 2.993767e+01 403 | -9.019951e+01 3.862774e+01 404 | -7.786070e+01 4.079373e+01 405 | -1.068364e+02 4.048785e+01 406 | -8.098966e+01 4.648959e+01 407 | -7.614739e+01 4.304999e+01 408 | -8.428065e+01 3.043978e+01 409 | -8.263932e+01 2.777118e+01 410 | -1.078109e+02 3.793811e+01 411 | -1.286022e+02 5.451586e+01 412 | -9.403216e+01 3.343092e+01 413 | -9.618077e+01 4.811913e+01 414 | -8.924548e+01 4.838170e+01 415 | -8.132895e+01 4.847608e+01 416 | -8.353626e+01 4.165381e+01 417 | -7.938533e+01 4.364856e+01 418 | -8.561584e+01 4.476000e+01 419 | -8.240871e+01 3.648055e+01 420 | -1.109698e+02 3.222155e+01 421 | -9.599335e+01 3.614975e+01 422 | -8.870783e+01 3.425741e+01 423 | -1.144696e+02 4.255620e+01 424 | -9.529430e+01 3.234748e+01 425 | -1.063827e+02 3.964499e+01 426 | -7.100332e+01 4.538351e+01 427 | -1.463534e+02 6.113036e+01 428 | -8.328093e+01 3.083473e+01 429 | -1.231140e+02 4.926044e+01 430 | -1.233645e+02 4.842831e+01 431 | -9.700357e+01 2.880597e+01 432 | -6.686864e+01 5.289737e+01 433 | -9.714951e+01 3.157182e+01 434 | -1.183424e+02 4.607038e+01 435 | -7.703196e+01 3.889037e+01 436 | -9.234231e+01 4.249296e+01 437 | -7.591316e+01 4.397559e+01 438 | -9.710574e+01 4.490059e+01 439 | -8.963031e+01 4.495954e+01 440 | -1.203049e+02 4.741419e+01 441 | -8.005269e+01 2.671439e+01 442 | -7.373383e+01 4.112398e+01 443 | -1.350532e+02 6.072087e+01 444 | -9.850099e+01 3.390831e+01 445 | -9.733558e+01 3.768698e+01 446 | -7.565200e+01 4.142286e+01 447 | -1.221411e+02 5.212865e+01 448 | -7.700141e+01 4.124095e+01 449 | -1.036219e+02 4.814703e+01 450 | -7.794599e+01 3.423497e+01 451 | -8.303391e+01 4.231782e+01 452 | -9.714074e+01 4.989943e+01 453 | -1.079551e+02 4.401684e+01 454 | -1.205070e+02 4.660413e+01 455 | -1.143713e+02 6.245432e+01 456 | -1.146246e+02 3.268485e+01 457 | -------------------------------------------------------------------------------- /src/spectral.jl: -------------------------------------------------------------------------------- 1 | import LinearAlgebra: checksquare, BlasInt 2 | import Printf 3 | 4 | 5 | ## Todo 6 | # 1. Add method for partial sweep cut 7 | # 2. Add a "SymmetricMatrix" wrapper to avoid 8 | # constant symmetry checks. 9 | # 3. Avoid creating the Laplacian matrix directly 10 | 11 | """ 12 | `_symeigs_smallest_arpack` 13 | --- 14 | Compute eigenvalues and vectors using direct calls to the ARPACK wrappers 15 | to get type-stability. This function works for symmetric matrices. 16 | 17 | This function works on Float32 and Float64-valued sparse matrices. 18 | It returns the smallest set of eigenvalues and vectors. 19 | 20 | It only works on matrices with more than 21 rows and columns. 21 | (Use a dense eigensolver for smaller problems.) 22 | 23 | Functions 24 | --------- 25 | - `(evals,evecs) = _symeigs_smallest_arpack(A::SparseMatrixCSC{V,Int}, 26 | nev::Int,tol::V,maxiter::Int, v0::Vector{V})` 27 | Inputs 28 | ------ 29 | - `A`: the sparse matrix, must be symmetric 30 | - `nev`: the number of eigenvectors requested 31 | - `tol`: the relative tolerance of the eigenvalue computation 32 | - `maxiter`: the maximum number of restarts 33 | - `v0`: the initial vector for the Lanczos process 34 | 35 | Example 36 | ------- 37 | This is an internal function. 38 | """ 39 | function _symeigs_smallest_arpack( 40 | A::SparseMatrixCSC{V,Int},nev::Int,tol::V,maxiter::Int, 41 | v0::Vector{V}) where V 42 | 43 | n::Int = checksquare(A) # get the size 44 | @assert n >= 21 45 | 46 | # setup options 47 | mode = 1 48 | sym = true 49 | iscmplx = false 50 | bmat = String("I") 51 | ncv = min(max(2*nev,20),n-1) 52 | 53 | whichstr = String("SA") 54 | ritzvec = true 55 | sigma = 0. 56 | 57 | #TOL = Array{V}(undef,1) 58 | #TOL[1] = tol 59 | TOL = Ref(tol) 60 | lworkl = ncv*(ncv + 8) 61 | v = Array{V}(undef, n, ncv) 62 | workd = Array{V}(undef, 3*n) 63 | workl = Array{V}(undef, lworkl) 64 | resid = Array{V}(undef, n) 65 | 66 | resid[:] = v0[:] 67 | 68 | info = zeros(BlasInt, 1) 69 | info[1] = 1 70 | 71 | iparam = zeros(BlasInt, 11) 72 | ipntr = zeros(BlasInt, 11) 73 | ido = zeros(BlasInt, 1) 74 | 75 | iparam[1] = BlasInt(1) 76 | iparam[3] = BlasInt(maxiter) 77 | iparam[7] = BlasInt(mode) 78 | 79 | # this is a helpful indexing vector 80 | zernm1 = 0:(n-1) 81 | 82 | while true 83 | # This is the reverse communication step that ARPACK does 84 | # we need to extract the desired vector and multiply it by A 85 | # unless the code says to stop 86 | Arpack.saupd( 87 | ido, bmat, n, whichstr, nev, TOL, resid, ncv, v, n, 88 | iparam, ipntr, workd, workl, lworkl, info) 89 | 90 | load_idx = ipntr[1] .+ zernm1 91 | store_idx = ipntr[2] .+ zernm1 92 | 93 | x = workd[load_idx] 94 | 95 | if ido[1] == 1 96 | workd[store_idx] = A*x 97 | elseif ido[1] == 99 98 | break 99 | else 100 | error("unexpected ARPACK behavior") 101 | end 102 | end 103 | 104 | # Once the process terminates, we need to extract the 105 | # eigenvectors. 106 | 107 | # calls to eupd 108 | howmny = String("A") 109 | select = Array{BlasInt}(undef, ncv) 110 | 111 | d = Array{V}(undef, nev) 112 | sigmar = ones(V,1)*sigma 113 | ldv = n 114 | Arpack.seupd(ritzvec, howmny, select, d, v, ldv, sigmar, 115 | bmat, n, whichstr, nev, TOL, resid, ncv, v, ldv, 116 | iparam, ipntr, workd, workl, lworkl, info) 117 | if info[1] != 0 118 | error("unexpected ARPACK exception") 119 | end 120 | 121 | # Now we want to return them in sorted order (smallest first) 122 | p = sortperm(d) 123 | 124 | d = d[p] 125 | vectors = v[1:n,p] 126 | 127 | return (d,vectors,iparam[5]) 128 | end 129 | 130 | """ 131 | `fiedler_vector` 132 | ---------------- 133 | 134 | Compute the Fiedler vector associated with the normalized Laplacian 135 | of the graph with adjacency matrix A. 136 | 137 | This function works with Float32 and Float64-valued sparse 138 | matrices and vectors. 139 | 140 | It requires a symmetric input matrix representing 141 | the adjacency matrix of an undirected graph. If the input 142 | is a disconnected network then the result may not 143 | 144 | The return vector is signed so that the number of positive 145 | entries is at least the number of negative entries. This will 146 | always give a unique, deterministic output 147 | in the case of a non-repeated second eigenvalue. 148 | 149 | Functions 150 | --------- 151 | - `(v,lam2) = fiedler_vector(A::SparseMatrixCSC{V,Int})` 152 | - `(v,lam2) = fiedler_vector(A::MatrixNetwork{V,Int})` 153 | 154 | Inputs 155 | ------ 156 | - `A`: the adjacency matrix 157 | - Optional Keywoard Inputs 158 | - `checksym`: ensure the matrix is symmetric 159 | - `tol`: the residual tolerance in the eigenvalue, eigenvector estimate. 160 | (This is an absolute error) 161 | - `maxiter`: the maximum iteration for ARPACK 162 | - `nev`: the number of eigenvectors estimated by ARPACK 163 | - `dense`: the threshold for a dense (LAPACK) computation 164 | instead of a sparse (ARPACK) computation. If the size of 165 | the matrix is less than `dense` then maxiter and nev are 166 | ignored and LAPACK computes all the eigenvalues. 167 | 168 | Returns 169 | ------- 170 | - `v`: The Fiedler vector as an array 171 | - `lam2`: the eigenvalue itself 172 | 173 | Example 174 | ------- 175 | ~~~ 176 | # construct an n-node path graph 177 | n = 25 178 | A = sparse(1:n-1,2:n,1.,n,n) 179 | A = A + A' 180 | (v,lam2) = fiedler_vector(A) # returns a cosine-like fiedler vector 181 | # using UnicodePlots; lineplot(v) 182 | ~~~ 183 | """ 184 | function fiedler_vector(A::SparseMatrixCSC{V,Int}; 185 | tol=1e-12,maxiter=300,dense=96,nev=2,checksym=true) where V 186 | n = checksquare(A) 187 | if checksym 188 | if !issymmetric(A) 189 | throw(ArgumentError("The input matrix must be symmetric.")) 190 | end 191 | end 192 | 193 | d = vec(sum(A,dims=1)) 194 | d = sqrt.(d) 195 | 196 | if n == 1 197 | X = zeros(V,1,2) 198 | lam2 = 0. 199 | elseif n <= dense 200 | ai,aj,av = findnz(A) 201 | L = sparse(ai,aj,-av./((d[ai].*d[aj])),n,n) # applied sqrt above 202 | L = Matrix(L) + 2I 203 | F = eigen!(Symmetric(L)) 204 | lam2 = F.values[2]-1. 205 | X = F.vectors 206 | else 207 | ai,aj,av = findnz(A) 208 | L = sparse(ai,aj,-av./((d[ai].*d[aj])),n,n) # applied sqrt above 209 | L = L + sparse(2.0I,n,n) 210 | 211 | (lams,X,nconv) = _symeigs_smallest_arpack(L,nev,tol,maxiter,d) 212 | lam2 = lams[2]-1. 213 | end 214 | x1err = norm(X[:,1]*sign(X[1,1]) - d/norm(d)) 215 | if x1err >= sqrt(tol) 216 | s = @sprintf(""" 217 | the null-space vector associated with the normalized Laplacian 218 | was computed inaccurately (diff=%.3e); the Fiedler vector is 219 | probably wrong or the graph is disconnected""",x1err) 220 | @warn s 221 | end 222 | 223 | x = vec(X[:,2]) 224 | if n > 1 225 | x = x./d # applied sqrt above 226 | end 227 | 228 | # flip the sign if the number of pos. entries is less than the num of neg. entries 229 | nneg = sum(x .< 0.) 230 | if n-nneg < nneg 231 | x = -x; 232 | end 233 | 234 | return (x,lam2) 235 | end 236 | 237 | fiedler_vector(A::MatrixNetwork{T};kwargs...) where {T} = 238 | fiedler_vector(sparse_transpose(A);kwargs...) # sparse_transpose converts directly 239 | 240 | """ 241 | RankedArray 242 | ----------- 243 | 244 | This is a data-type that functions as a union between 245 | a sparse and dense array. It allows us to check if 246 | an element is not in the array (i.e. it's too long) 247 | by adding a getindex function to the array type. 248 | 249 | It's called a RankedArray because the idea is that 250 | the elements of the array are the ranks of the 251 | nodes in a sorted vector. 252 | 253 | This should not be used externally. 254 | 255 | Example 256 | ------- 257 | v = rand(10) 258 | r = RankedArray(sortperm(v)) 259 | haskey(r,11) # returns false 260 | haskey(r,1) # returns true 261 | haskey(r,0) # returns false 262 | """ 263 | mutable struct RankedArray{S} 264 | data::S 265 | end 266 | 267 | import Base: getindex, haskey 268 | 269 | haskey(A::RankedArray{S}, x::Int) where {S} = x >= 1 && x <= length(A.data) 270 | getindex(A::RankedArray{S}, i) where {S} = A.data[i] 271 | 272 | """ 273 | SweepcutProfile 274 | --------------- 275 | 276 | This type is the result of a sweepcut operation. It stores 277 | a number of vectors associated with a sweep cut including 278 | the cut and volume associated with each. 279 | 280 | Methods 281 | ------- 282 | - `bestset(P::SweepcutProfile)` return the best set identified 283 | in the sweepcut. This is usually what you want. 284 | 285 | Example 286 | ------- 287 | See `sweepcut` 288 | 289 | See also 290 | -------- 291 | * `spectral_cut` 292 | 293 | """ 294 | struct SweepcutProfile{V,F} 295 | p::Vector{Int} 296 | conductance::Vector{F} 297 | cut::Vector{V} 298 | volume::Vector{V} 299 | total_volume::V 300 | total_nodes::Int 301 | 302 | function SweepcutProfile{V,F}(p::Vector{Int},nnodes::Int,totalvol::V) where {V,F} 303 | n = length(p) 304 | new{V,F}(p,Array{F}(undef,n-1),Array{V}(undef,n-1),Array{V}(undef,n-1),totalvol,nnodes) 305 | end 306 | end 307 | 308 | """ 309 | `sweepcut` 310 | -------- 311 | 312 | A sweepcut is an operation that takes an order to 313 | the vertices of a graph and evaluates the cut and 314 | volume of every partition induced by a prefix of 315 | that ordering. That is, if the order of vertices 316 | is 317 | 318 | v1, v2, v3, ... 319 | 320 | the the sweep cut evaluates 321 | 322 | cut({v1}), vol({v1}) 323 | cut({v1,v2}), vol({v1,v2}) 324 | cut({v1,v2,v3}), vol({v1,v2,v3}) 325 | ... 326 | 327 | where cut is the total edge weight that connects 328 | vertices in S to the graph. And vol is the 329 | total edge weight connecting 330 | 331 | Functions 332 | --------- 333 | - `profile = sweepcut(A,x::Vector{T})` 334 | A standard sweepcut call that will get a sweepcut 335 | for a vector where x is sorted to get the order 336 | of the vertices (decreasing order). 337 | This is useful if you have a vector that should 338 | indicate good cuts in the graph, such as 339 | the Fiedler vector. 340 | 341 | - `profile = sweepcut(A,p,r,totalvol,maxvol)` 342 | the in-depth call that all others are converted into. 343 | We strongly recommend against calling this yourself 344 | unless you understand the sweepcut code. 345 | 346 | Inputs 347 | ------ 348 | - `A`: the sparse matrix representing the symmetric graph 349 | - `x`: A vector scoring each vertex. This will be sorted and 350 | turned into one of the other inputs. 351 | - `p`: a partial permutation vector over the vertices of the graph 352 | This vector needs to list every vertex at most once. 353 | It could be shorter and need not list every vertex. 354 | - `r`: A general data structure that gives 355 | the rank of an item in the permutation vector 356 | p should be sorted in decreasing order so that 357 | i < j means that r[p[i]] < r[p[j]] 358 | - `totalvol`: the total volume of the graph 359 | - `maxvol`: the maximum volume of any set considered 360 | 361 | Returns 362 | ------- 363 | A `SweepcutProfile` type with all the information computed 364 | in the sweepcut. Most likely, you want the result `bestset` 365 | as indicated below. 366 | 367 | Example 368 | ------- 369 | ~~~~ 370 | A = load_matrix_network("minnesota") 371 | v = fiedler_vector(A)[1] # get the 372 | p = sweepcut(A,v) 373 | S = bestset(p) # get the bestset from the profile 374 | T = spectral_cut(A).set # should give you the same set 375 | # using UnicodePlots; lineplot(p.conductance) # show the conductance 376 | ~~~~ 377 | """ 378 | function sweepcut(A::SparseMatrixCSC{V,Int}, p::Vector{Int}, r, totalvol::V, maxvol::T) where {V,T} 379 | 380 | n = checksquare(A) 381 | nlast = length(p) 382 | 383 | if n < nlast 384 | throw(DimensionMismatch( 385 | "the permutation vector is too long and should have fewer entries than the graph")) 386 | end 387 | 388 | F = typeof(one(V)/one(V)) # find the conductance type 389 | output = SweepcutProfile{V,F}(p,n,totalvol) 390 | 391 | cut = zero(V) 392 | vol = zero(V) 393 | colptr = A.colptr 394 | rowval = rowvals(A) 395 | nzval = A.nzval 396 | 397 | for (i,v) in enumerate(p) 398 | deltain = zero(V) # V might be pos. only... so delay subtraction 399 | deg = zero(V) 400 | rankv = getindex(r,v) 401 | for nzi in colptr[v]:(colptr[v+1] - 1) 402 | nbr = rowval[nzi] 403 | deg += nzval[nzi] 404 | if haskey(r,nbr) # our neighbor is ranked 405 | if getindex(r,nbr) <= rankv # nbr is ranked lower, decrease cut 406 | deltain += v == nbr ? nzval[nzi] : 2*nzval[nzi] 407 | end 408 | end 409 | end 410 | cut += deg 411 | cut -= deltain 412 | vol += deg 413 | 414 | # don't assign final values because they are unhelpful 415 | if i==nlast 416 | break 417 | end 418 | 419 | cond = cut/min(vol,totalvol-vol) 420 | output.conductance[i] = cond 421 | output.cut[i] = cut 422 | output.volume[i] = vol 423 | end 424 | 425 | if nlast == size(A,1) 426 | @assert abs(cut) <= 1e-12*totalvol 427 | end 428 | return output 429 | end 430 | 431 | function sweepcut(A::SparseMatrixCSC{V,Int}, x::Vector{T}, vol::V) where {V,T} 432 | p = sortperm(x,rev=true) 433 | ranks = Array{Int}(undef, length(x)) 434 | for (i,v) in enumerate(p) 435 | ranks[v] = i 436 | end 437 | r = RankedArray(ranks) 438 | return sweepcut(A, p, r, vol, Inf) 439 | end 440 | 441 | sweepcut(A::SparseMatrixCSC{V,Int}, x::Vector{T}) where {V,T} = 442 | sweepcut(A, x, sum(A)) 443 | 444 | function bestset(prof::SweepcutProfile{V,F}) where {V,F} 445 | nnodes = 0 446 | if !isempty(prof.conductance) 447 | bsetind = argmin(prof.conductance) 448 | bsetvol = prof.volume[bsetind] 449 | nnodes = bsetind 450 | if bsetvol > prof.total_volume - bsetvol 451 | # we want the complement set 452 | nnodes = prof.total_nodes - bsetind 453 | end 454 | end 455 | 456 | bset = Vector{Int}(undef,nnodes) 457 | if isempty(prof.conductance) 458 | elseif bsetvol > prof.total_volume - bsetvol 459 | # ick, we need the complement 460 | bset[:] = collect(setdiff(BitSet(Int(1):Int(prof.total_nodes)),prof.p[1:bsetind])) 461 | else 462 | # easy 463 | bset[:] = prof.p[1:bsetind] 464 | end 465 | return bset 466 | end 467 | 468 | """ 469 | `SpectralCut` 470 | ------------- 471 | The return type from the `spectral_cut` 472 | 473 | Fields 474 | ------ 475 | - `set`: the small side of the spectral cut 476 | - `A`: the network of the largest strong component of the network 477 | - `lam2`: the eigenvalue of the normalized Laplacian 478 | - `x`: the Fiedler vector for spectral partitioning 479 | - `sweepcut_profile`: the sweepcut profile output 480 | - `comps`: the output from the strong_components function 481 | check comps.number to get the number of components 482 | - `largest_component`: the index of the largest strong component 483 | 484 | The most useful outputs are `set` and `lam2`; the others are provided 485 | for experts who wish to use some of the diagonstics provided. 486 | """ 487 | struct SpectralCut{V,F} 488 | set::Vector{Int} 489 | A::SparseMatrixCSC{V,Int} 490 | lam2::F 491 | x::Vector{Float64} 492 | sweepcut_profile::SweepcutProfile{V,F} 493 | comps::MatrixNetworks.Strong_components_output 494 | largest_component::Int 495 | end 496 | 497 | function show(io::IO,obj::SpectralCut{V,F}) where {V,F} 498 | println(io,"Spectral cut on adjacency matrix with $(size(obj.A,1)) nodes and $(nnz(obj.A)) non-zeros") 499 | if obj.comps.number > 1 500 | println(io, " formed from the largest connected component of the input matrix") 501 | end 502 | println(io," conductance = $(minimum(obj.sweepcut_profile.conductance))") 503 | println(io," size = $(length(obj.set))") 504 | end 505 | 506 | """ 507 | `spectral_cut` 508 | -------------- 509 | `spectral_cut` will produce a spectral cut partition of the graph into 510 | two pieces. 511 | 512 | Special cases 513 | * if your graph is disconnected, then we will partition it into the 514 | largest connected component (chosen arbitrary if there are multiple) 515 | and produce a cut of just the largest connected component. 516 | * if your graph is a single node, we will partition it into the empty 517 | cut. 518 | 519 | Output 520 | ------ 521 | The output has type SpectralCut 522 | We always return the smaller side of the cut in terms of total volume. 523 | 524 | Inputs 525 | ------ 526 | - `A`: The sparse matrix or martrix network that you want to 527 | find the spectral cut for. 528 | - `checksym`: Should we check symmetry? 529 | Don't set this to false unless you have checked symmetry 530 | in another call. *This may go away in future interfaces* 531 | - `ccwarn`: Turn off the warning for disconnected graphs. 532 | This useful in larger subroutines where this is handled 533 | through another mechanism. 534 | """ 535 | function spectral_cut(A::SparseMatrixCSC{V,Int},checksym::Bool,ccwarn::Bool) where V 536 | n = checksquare(A) 537 | if checksym 538 | if !issymmetric(A) 539 | throw(ArgumentError("The input matrix must be symmetric.")) 540 | end 541 | end 542 | 543 | # test for non-negativity 544 | if !all(nonzeros(A) .>= 0) 545 | throw(ArgumentError("The input matrix must be non-negative")) 546 | end 547 | 548 | # need to test for components 549 | G = MatrixNetwork(n,A.colptr,A.rowval,A.nzval) 550 | cc = scomponents(G) 551 | lccind = argmax(cc.sizes) 552 | B = A 553 | if cc.number > 1 554 | if ccwarn 555 | @warn "The input matrix had $(cc.number) components, running on largest..." 556 | end 557 | f = cc.map .== lccind 558 | B = A[f,f] # using logical indexing is faster than find for large sets (2015/11/14) 559 | # n=10^5;A=sprand(n,n,25/n);f=rand(n).>0.01;s=find(f);@time sum(A);@time A[f,f]; @time A[s,s]; 560 | subset = findall(f) 561 | end 562 | 563 | # run the partition 564 | x,lam2 = fiedler_vector(B;checksym=false) 565 | 566 | totalvol = sum(B) 567 | sweep = sweepcut(B,x,totalvol) 568 | bset = bestset(sweep) 569 | 570 | if cc.number > 1 571 | # map the set back to the original index set 572 | bset = subset[bset] 573 | end 574 | 575 | return SpectralCut(bset,B,lam2,x,sweep,cc,lccind) 576 | end 577 | 578 | function spectral_cut(A::SparseMatrixCSC{V,Int}) where V 579 | return spectral_cut(A,true,true) 580 | end 581 | 582 | function spectral_cut(A::MatrixNetwork{V}) where V 583 | return spectral_cut(sparse_transpose(A),true,true) 584 | end 585 | --------------------------------------------------------------------------------