Skip to content

Commit 245508e

Browse files
authored
Merge pull request #1 from simonschoelly/v1.0_fix
Fixes for Julia v1.0 and v0.7
2 parents a5f466d + 875a53f commit 245508e

File tree

4 files changed

+87
-49
lines changed

4 files changed

+87
-49
lines changed

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ os:
44
- linux
55
# - osx
66
julia:
7-
- 0.6
7+
- 0.7
8+
. 1.0
89
- nightly
910

1011
matrix:
@@ -35,4 +36,4 @@ git:
3536
# - julia -e 'Pkg.clone(pwd()); Pkg.build("CommunityDetection"); Pkg.test("CommunityDetection"; coverage=true)'
3637
after_success:
3738
# push coverage results to Codecov
38-
- julia -e 'cd(Pkg.dir("CommunityDetection")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'
39+
- julia -e 'cd(using Pkg; Pkg.dir("CommunityDetection")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'

REQUIRE

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
julia 0.6
2-
LightGraphs 0.9
1+
julia 0.7
2+
LightGraphs 1.1.0
33
Clustering
4+
ArnoldiMethod

src/CommunityDetection.jl

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
__precompile__(true)
21
module CommunityDetection
32
using LightGraphs
4-
import Clustering: kmeans
3+
using ArnoldiMethod: LR, SR
4+
using LinearAlgebra: I, Diagonal
5+
using Clustering: kmeans
56

67
export community_detection_nback, community_detection_bethe
78

@@ -19,7 +20,9 @@ non-backtracking matrix of `g`.
1920
function community_detection_nback(g::AbstractGraph, k::Int)
2021
#TODO insert check on connected_components
2122
ϕ = real(nonbacktrack_embedding(g, k))
22-
if k==2
23+
if k == 1
24+
c = fill(1, nv(g))
25+
elseif k==2
2326
c = community_detection_threshold(g, ϕ[1,:])
2427
else
2528
c = kmeans(ϕ, k).assignments
@@ -54,8 +57,8 @@ See `Nonbacktracking` for details.
5457
"""
5558
function nonbacktrack_embedding(g::AbstractGraph, k::Int)
5659
B = Nonbacktracking(g)
57-
λ, eigv, conv = eigs(B, nev=k+1, v0=ones(Float64, B.m))
58-
ϕ = zeros(Complex64, nv(g), k-1)
60+
λ, eigv = LightGraphs.LinAlg.eigs(B, nev=k+1, which=LR())
61+
ϕ = zeros(ComplexF32, nv(g), k-1)
5962
# TODO decide what to do with the stationary distribution ϕ[:,1]
6063
# this code just throws it away in favor of eigv[:,2:k+1].
6164
# we might also use the degree distribution to scale these vectors as is
@@ -84,19 +87,23 @@ Return a vector containing the vertex assignments.
8487
"""
8588
function community_detection_bethe(g::AbstractGraph, k::Int=-1; kmax::Int=15)
8689
A = adjacency_matrix(g)
87-
D = diagm(degree(g))
90+
D = Diagonal(degree(g))
8891
r = (sum(degree(g)) / nv(g))^0.5
8992

90-
Hr = (r^2-1)*eye(nv(g))-r*A+D;
91-
# Hmr = (r^2-1)*eye(nv(g))+r*A+D;
93+
Hr = Matrix((r^2-1)*I, nv(g), nv(g)) - r*A + D;
94+
#Hmr = Matrix((r^2-1)*I, nv(g), nv(g)) + r*A + D;
9295
k >= 1 && (kmax = k)
93-
λ, eigv = eigs(Hr, which=:SR, nev=min(kmax, nv(g)))
94-
q = findlast(x -> x<0, λ)
95-
k > q && warn("Using eigenvectors with positive eigenvalues,
96+
λ, eigv = LightGraphs.LinAlg.eigs(Hr, which=SR(), nev=min(kmax, nv(g)))
97+
98+
# TODO eps() is chosen quite arbitrarily here, because some of eigenvalues
99+
# don't convert exactly to zero as they should. Some analysis could show
100+
# what threshold should be used instead
101+
q = something(findlast(x -> (x < -eps()), λ), 0)
102+
k > q && @warn("Using eigenvectors with positive eigenvalues,
96103
some communities could be meaningless. Try to reduce `k`.")
97104
k < 1 && (k = q)
98-
k < 1 && return fill(1, nv(g))
99-
labels = kmeans(eigv[:,2:k]', k).assignments
105+
k <= 1 && return fill(1, nv(g))
106+
labels = kmeans(collect(transpose(eigv[:,2:k])), k).assignments
100107
return labels
101108
end
102109

test/runtests.jl

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
using CommunityDetection
22
using LightGraphs
3-
using Base.Test
3+
using LinearAlgebra: I, norm
4+
using ArnoldiMethod: LR
5+
using SparseArrays: sparse
6+
using Test
47

58
""" Spectral embedding of the non-backtracking matrix of `g`
69
(see [Krzakala et al.](http://www.pnas.org/content/110/52/20935.short)).
@@ -12,8 +15,8 @@ return : a matrix ϕ where ϕ[:,i] are the coordinates for vertex i.
1215
"""
1316
function nonbacktrack_embedding_dense(g::AbstractGraph, k::Int)
1417
B, edgeid = non_backtracking_matrix(g)
15-
λ,eigv,conv = eigs(B, nev=k+1, v0=ones(Float64, size(B,1)))
16-
ϕ = zeros(Complex64, k-1, nv(g))
18+
λ, eigv = LightGraphs.LinAlg.eigs(B, nev=k+1, which=LR())
19+
ϕ = zeros(ComplexF32, k-1, nv(g))
1720
# TODO decide what to do with the stationary distribution ϕ[:,1]
1821
# this code just throws it away in favor of eigv[:,2:k+1].
1922
# we might also use the degree distribution to scale these vectors as is
@@ -30,6 +33,8 @@ function nonbacktrack_embedding_dense(g::AbstractGraph, k::Int)
3033
return ϕ
3134
end
3235

36+
@testset "CommunityDetection" begin
37+
3338
n = 10; k = 5
3439
pg = PathGraph(n)
3540
ϕ1 = CommunityDetection.nonbacktrack_embedding(pg, k)'
@@ -46,48 +51,72 @@ z = B * x
4651
@test norm(y-z) < 1e-8
4752

4853
#check that matmat works and full(nbt) == B
49-
@test norm(nbt*eye(nbt.m) - B) < 1e-8
54+
@test norm(nbt*Matrix{Float64}(I, nbt.m, nbt.m) - B) < 1e-8
5055

5156
#check that we can use the implicit matvec in nonbacktrack_embedding
5257
@test size(y) == size(x)
5358
ϕ2 = nonbacktrack_embedding_dense(pg, k)'
5459
@test size(ϕ2) == size(ϕ1)
5560

5661
#check that this recovers communities in the path of cliques
57-
n=10
58-
g10 = CompleteGraph(n)
59-
z = copy(g10)
60-
for k=2:5
61-
z = blkdiag(z, g10)
62-
add_edge!(z, (k-1)*n, k*n)
63-
64-
c = community_detection_nback(z, k)
65-
@test sort(union(c)) == [1:k;]
66-
a = collect(n:n:k*n)
67-
@test length(c[a]) == length(unique(c[a]))
68-
for i=1:k
69-
for j=(i-1)*n+1:i*n
70-
@test c[j] == c[i*n]
62+
@testset "community_detection_nback(z, k)" begin
63+
n=10
64+
g10 = CompleteGraph(n)
65+
z = copy(g10)
66+
for k=2:5
67+
z = blockdiag(z, g10)
68+
add_edge!(z, (k-1)*n, k*n)
69+
70+
c = community_detection_nback(z, k)
71+
@test sort(union(c)) == [1:k;]
72+
a = collect(n:n:k*n)
73+
@test length(c[a]) == length(unique(c[a]))
74+
for i=1:k
75+
cluster_range = (1:n) .+ (i-1)*n
76+
@test length(unique(c[cluster_range])) == 1
7177
end
7278
end
79+
end
7380

74-
c = community_detection_bethe(z, k)
75-
@test sort(union(c)) == [1:k;]
76-
a = collect(n:n:k*n)
77-
@test length(c[a]) == length(unique(c[a]))
78-
for i=1:k
79-
for j=(i-1)*n+1:i*n
80-
@test c[j] == c[i*n]
81+
@testset "community_detection_bethe(z, k)" begin
82+
n=10
83+
g10 = CompleteGraph(n)
84+
z = copy(g10)
85+
for k=2:5
86+
z = blockdiag(z, g10)
87+
add_edge!(z, (k-1)*n, k*n)
88+
89+
c = community_detection_bethe(z, k)
90+
@test sort(union(c)) == [1:k;]
91+
a = collect(n:n:k*n)
92+
@test length(c[a]) == length(unique(c[a]))
93+
94+
for i=1:k
95+
cluster_range = (1:n) .+ (i-1)*n
96+
@test length(unique(c[cluster_range])) == 1
8197
end
98+
8299
end
100+
end
101+
102+
@testset "community_detection_bethe(z)" begin
103+
n=10
104+
g10 = CompleteGraph(n)
105+
z = copy(g10)
106+
for k=2:5
107+
z = blockdiag(z, g10)
108+
add_edge!(z, (k-1)*n, k*n)
109+
110+
c = community_detection_bethe(z)
111+
@test sort(union(c)) == [1:k;]
112+
a = collect(n:n:k*n)
113+
@test length(c[a]) == length(unique(c[a]))
83114

84-
c = community_detection_bethe(z)
85-
@test sort(union(c)) == [1:k;]
86-
a = collect(n:n:k*n)
87-
@test length(c[a]) == length(unique(c[a]))
88-
for i=1:k
89-
for j=(i-1)*n+1:i*n
90-
@test c[j] == c[i*n]
115+
for i=1:k
116+
cluster_range = (1:n) .+ (i-1)*n
117+
@test length(unique(c[cluster_range])) == 1
91118
end
92119
end
93120
end
121+
122+
end

0 commit comments

Comments
 (0)