Skip to content

Commit 977cad5

Browse files
committed
some refactoring & fixing a bug in spectral_layout
1 parent adeda23 commit 977cad5

File tree

4 files changed

+67
-74
lines changed

4 files changed

+67
-74
lines changed

src/collapse_plot.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using GraphPlot
22

3-
function collapse_graph(g::AbstractGraph{T}, membership::Vector{Int}) where {T<:Integer}
3+
function collapse_graph(g::AbstractGraph, membership::Vector{Int})
44
nb_comm = maximum(membership)
55

66
collapsed_edge_weights = Vector{Dict{Int,Float64}}(undef, nb_comm)
@@ -36,7 +36,7 @@ function collapse_graph(g::AbstractGraph{T}, membership::Vector{Int}) where {T<:
3636
end
3737
end
3838

39-
collapsed_graph, collapsed_weights
39+
return collapsed_graph, collapsed_weights
4040
end
4141

4242
function community_layout(g::AbstractGraph, membership::Vector{Int})
@@ -60,10 +60,10 @@ function community_layout(g::AbstractGraph, membership::Vector{Int})
6060
ly[node] = 1.8*length(nodes)/N*sin(θ[idx]) + cly[lbl]
6161
end
6262
end
63-
lx, ly
63+
return lx, ly
6464
end
6565

66-
function collapse_layout(g::AbstractGraph{T}, membership::Vector{Int}) where {T<:Integer}
66+
function collapse_layout(g::AbstractGraph, membership::Vector{Int})
6767
lightg = LightGraphs.SimpleGraph(nv(g))
6868
for e in edges(g)
6969
u = src(e)
@@ -92,5 +92,5 @@ function collapse_layout(g::AbstractGraph{T}, membership::Vector{Int}) where {T<
9292
ly[node] = 1.8*length(nodes)/N*subly[idx] + cly[lbl]
9393
end
9494
end
95-
lx, ly
95+
return lx, ly
9696
end

src/layout.jl

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using SparseArrays: SparseMatrixCSC, sparse
2-
using ArnoldiMethod: LR
2+
using ArnoldiMethod: SR
3+
using Base: OneTo
34
using LinearAlgebra: eigen
45

56
"""
@@ -39,7 +40,7 @@ Position nodes on a circle.
3940
4041
**Parameters**
4142
42-
*G*
43+
*g*
4344
a graph
4445
4546
**Returns**
@@ -51,7 +52,7 @@ but will be normalized and centered anyway
5152
**Examples**
5253
5354
```
54-
julia> g = simple_house_graph()
55+
julia> g = smallgraph(:house)
5556
julia> locs_x, locs_y = circular_layout(g)
5657
```
5758
"""
@@ -60,18 +61,18 @@ function circular_layout(g)
6061
return [0.0], [0.0]
6162
else
6263
# Discard the extra angle since it matches 0 radians.
63-
θ = range(0, stop=2pi, length=_nv(G)+1)[1:end-1]
64+
θ = range(0, stop=2pi, length=nv(g)+1)[1:end-1]
6465
return cos.(θ), sin.(θ)
6566
end
6667
end
6768

6869
"""
6970
This function is copy from [IainNZ](https://github.com/IainNZ)'s [GraphLayout.jl](https://github.com/IainNZ/GraphLayout.jl)
7071
71-
Use the spring/repulsion model of Fruchterman and Reingold (1991):
72+
Use a modified version of the spring/repulsion model of Fruchterman and Reingold (1991):
7273
73-
+ Attractive force: f_a(d) = d^2 / k
74-
+ Repulsive force: f_r(d) = -k^2 / d
74+
+ Attractive force: f_a(d) = d / k
75+
+ Repulsive force: f_r(d) = -k^2 / d^2
7576
7677
where d is distance between two vertices and the optimal distance
7778
between vertices k is defined as C * sqrt( area / num_vertices )
@@ -93,63 +94,66 @@ Initial "temperature", controls movement per iteration
9394
9495
**Examples**
9596
```
96-
julia> g = graphfamous("karate")
97+
julia> g = smallgraph(:karate)
9798
julia> locs_x, locs_y = spring_layout(g)
9899
```
99100
"""
100-
function spring_layout(g::AbstractGraph{T}, locs_x=2*rand(nv(g)).-1.0, locs_y=2*rand(nv(g)).-1.0; C=2.0, MAXITER=100, INITTEMP=2.0) where {T<:Integer}
101-
102-
#size(adj_matrix, 1) != size(adj_matrix, 2) && error("Adj. matrix must be square.")
103-
N = nv(g)
101+
function spring_layout(g::AbstractGraph,
102+
locs_x=2*rand(nv(g)).-1.0,
103+
locs_y=2*rand(nv(g)).-1.0;
104+
C=2.0,
105+
MAXITER=100,
106+
INITTEMP=2.0)
107+
108+
nvg = nv(g)
104109
adj_matrix = adjacency_matrix(g)
105110

106111
# The optimal distance bewteen vertices
107-
K = C * sqrt(4.0 / N)
112+
k = C * sqrt(4.0 / nvg)
113+
= k * k
108114

109115
# Store forces and apply at end of iteration all at once
110-
force_x = zeros(N)
111-
force_y = zeros(N)
116+
force_x = zeros(nvg)
117+
force_y = zeros(nvg)
112118

113119
# Iterate MAXITER times
114120
@inbounds for iter = 1:MAXITER
115121
# Calculate forces
116-
for i = 1:N
122+
for i = 1:nvg
117123
force_vec_x = 0.0
118124
force_vec_y = 0.0
119-
for j = 1:N
125+
for j = 1:nvg
120126
i == j && continue
121127
d_x = locs_x[j] - locs_x[i]
122128
d_y = locs_y[j] - locs_y[i]
123-
d = sqrt(d_x^2 + d_y^2)
124-
if adj_matrix[i,j] != zero(eltype(adj_matrix)) || adj_matrix[j,i] != zero(eltype(adj_matrix))
125-
# F = d^2 / K - K^2 / d
126-
F_d = d / K - K^2 / d^2
129+
dist² = (d_x * d_x) + (d_y * d_y)
130+
dist = sqrt(dist²)
131+
132+
if !( iszero(adj_matrix[i,j]) && iszero(adj_matrix[j,i]) )
133+
# Attractive + repulsive force
134+
# F_d = dist² / k - k² / dist # original FR algorithm
135+
F_d = dist / k -/ dist²
127136
else
128137
# Just repulsive
129-
# F = -K^2 / d^
130-
F_d = -K^2 / d^2
138+
# F_d = - / dist # original FR algorithm
139+
F_d = - / dist²
131140
end
132-
# d / sin θ = d_y/d = fy/F
133-
# F /| dy fy -> fy = F*d_y/d
134-
# / | cos θ = d_x/d = fx/F
135-
# /--- -> fx = F*d_x/d
136-
# dx fx
137141
force_vec_x += F_d*d_x
138142
force_vec_y += F_d*d_y
139143
end
140144
force_x[i] = force_vec_x
141145
force_y[i] = force_vec_y
142146
end
143147
# Cool down
144-
TEMP = INITTEMP / iter
148+
temp = INITTEMP / iter
145149
# Now apply them, but limit to temperature
146-
for i = 1:N
147-
force_mag = sqrt(force_x[i]^2 + force_y[i]^2)
148-
scale = min(force_mag, TEMP)/force_mag
150+
for i = 1:nvg
151+
fx = force_x[i]
152+
fy = force_y[i]
153+
force_mag = sqrt((fx * fx) + (fy * fy))
154+
scale = min(force_mag, temp) / force_mag
149155
locs_x[i] += force_x[i] * scale
150-
#locs_x[i] = max(-1.0, min(locs_x[i], +1.0))
151156
locs_y[i] += force_y[i] * scale
152-
#locs_y[i] = max(-1.0, min(locs_y[i], +1.0))
153157
end
154158
end
155159

@@ -162,7 +166,7 @@ function spring_layout(g::AbstractGraph{T}, locs_x=2*rand(nv(g)).-1.0, locs_y=2*
162166
map!(z -> scaler(z, min_x, max_x), locs_x, locs_x)
163167
map!(z -> scaler(z, min_y, max_y), locs_y, locs_y)
164168

165-
return locs_x,locs_y
169+
return locs_x, locs_y
166170
end
167171

168172
"""
@@ -172,28 +176,27 @@ Position nodes in concentric circles.
172176
173177
**Parameters**
174178
175-
*G*
179+
*g*
176180
a graph
177181
178182
*nlist*
179183
Vector of Vector, Vector of node Vector for each shell.
180184
181185
**Examples**
182186
```
183-
julia> g = graphfamous("karate")
187+
julia> g = smallgraph(:karate)
184188
julia> nlist = Array{Vector{Int}}(2)
185189
julia> nlist[1] = [1:5]
186190
julia> nlist[2] = [6:num_vertiecs(g)]
187191
julia> locs_x, locs_y = shell_layout(g, nlist)
188192
```
189193
"""
190-
function shell_layout(G, nlist::Union{Nothing, Vector{Vector{Int}}} = nothing)
191-
if _nv(G) == 1
194+
function shell_layout(g, nlist::Union{Nothing, Vector{Vector{Int}}} = nothing)
195+
if nv(g) == 1
192196
return [0.0], [0.0]
193197
end
194198
if nlist == nothing
195-
nlist = Array{Vector{Int}}(1)
196-
nlist[1] = collect(1:_nv(G))
199+
nlist = [collect(1:nv(g))]
197200
end
198201
radius = 0.0
199202
if length(nlist[1]) > 1
@@ -208,7 +211,7 @@ function shell_layout(G, nlist::Union{Nothing, Vector{Vector{Int}}} = nothing)
208211
append!(locs_y, radius*sin.(θ))
209212
radius += 1.0
210213
end
211-
locs_x, locs_y
214+
return locs_x, locs_y
212215
end
213216

214217
"""
@@ -228,20 +231,20 @@ the edge weight. If None, then all edge weights are 1.
228231
229232
**Examples**
230233
```
231-
julia> g = graphfamous("karate")
234+
julia> g = smallgraph(:karate)
232235
julia> weight = rand(num_edges(g))
233236
julia> locs_x, locs_y = spectral_layout(g, weight)
234237
```
235238
"""
236-
function spectral_layout(g::AbstractGraph{T}, weight=nothing) where {T<:Integer}
239+
function spectral_layout(g::AbstractGraph, weight=nothing)
237240
if nv(g) == 1
238241
return [0.0], [0.0]
239242
elseif nv(g) == 2
240243
return [0.0, 1.0], [0.0, 0.0]
241244
end
242245

243246
if weight == nothing
244-
weight = ones(length(edges(g)))
247+
weight = ones(ne(g))
245248
end
246249
if nv(g) > 500
247250
A = sparse(Int[src(e) for e in edges(g)],
@@ -267,7 +270,7 @@ function _spectral(A::SparseMatrixCSC)
267270
data = vec(sum(A, dims=1))
268271
D = sparse(Base.OneTo(length(data)), Base.OneTo(length(data)), data)
269272
L = D - A
270-
eigenvalues, eigenvectors = LightGraphs.LinAlg.eigs(L, nev=3, which=LR())
273+
eigenvalues, eigenvectors = LightGraphs.LinAlg.eigs(L, nev=3, which=SR())
271274
index = sortperm(real(eigenvalues))[2:3]
272275
return real(eigenvectors[:, index[1]]), real(eigenvectors[:, index[2]])
273276
end

src/stress.jl

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,21 @@ Reference:
5555
pages={239--250},
5656
}
5757
"""
58-
function stressmajorize_layout(g::AbstractGraph, p::Int=2, w=nothing, X0=randn(nv(g), p);
59-
maxiter = 400size(X0, 1)^2, abstols=(eps(eltype(X0))),
60-
reltols=(eps(eltype(X0))), abstolx=(eps(eltype(X0))),
61-
verbose = false, returnall = false)
58+
function stressmajorize_layout(g::AbstractGraph,
59+
p::Int=2,
60+
w=nothing,
61+
X0=randn(nv(g), p);
62+
maxiter = 400size(X0, 1)^2,
63+
abstols=(eps(eltype(X0))),
64+
reltols=(eps(eltype(X0))),
65+
abstolx=(eps(eltype(X0))),
66+
verbose = false,
67+
returnall = false)
6268

6369
@assert size(X0, 2)==p
6470
δ = fill(1.0, nv(g), nv(g))
6571

66-
if w==nothing
72+
if w == nothing
6773
w = δ.^-2
6874
w[.!isfinite.(w)] .= 0
6975
end
@@ -137,7 +143,7 @@ function weightedlaplacian(w)
137143
Lw[i, j] = -w[i, j]
138144
D += w[i, j]
139145
end
140-
Lw[i, i]=D
146+
Lw[i, i] = D
141147
end
142148
return Lw
143149
end

test/runtests.jl

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,8 @@ using VisualRegressionTests
1515
istravis = "TRAVIS" keys(ENV)
1616
datadir = joinpath(@__DIR__, "data")
1717

18-
karate_edges = Edge.([
19-
1 => 2, 1 => 3, 1 => 4, 1 => 5, 1 => 6, 1 => 7,
20-
1 => 8, 1 => 9, 1 => 11, 1 => 12, 1 => 13, 1 => 14,
21-
1 => 18, 1 => 20, 1 => 22, 1 => 32, 2 => 3, 2 => 4,
22-
2 => 8, 2 => 14, 2 => 18, 2 => 20, 2 => 22, 2 => 31,
23-
3 => 4, 3 => 8, 3 => 9, 3 => 10, 3 => 14, 3 => 28,
24-
3 => 29, 3 => 33, 4 => 8, 4 => 13 , 4 => 14, 5 => 7,
25-
5 => 11, 6 => 7, 6 => 11, 6 => 17 , 7 => 17, 9 => 31,
26-
9 => 33, 9 => 34, 10 => 34, 14 => 34, 15 => 33, 15 => 34,
27-
16 => 33, 16 => 34, 19 => 33, 19 => 34, 20 => 34, 21 => 33,
28-
21 => 34, 23 => 33, 23 => 34, 24 => 26, 24 => 28, 24 => 30,
29-
24 => 33, 24 => 34, 25 => 26, 25 => 28, 25 => 32, 26 => 32,
30-
27 => 30, 27 => 34, 28 => 34, 29 => 32, 29 => 34, 30 => 33,
31-
30 => 34, 31 => 33, 31 => 34, 32 => 33, 32 => 34, 33 => 34,
32-
])
33-
3418
# graphs to test
35-
g = SimpleGraph(karate_edges)
19+
g = smallgraph(:karate)
3620
h = LightGraphs.WheelGraph(10)
3721

3822
# plot and save function for visual regression tests

0 commit comments

Comments
 (0)