@@ -72,9 +72,15 @@ The main equation to solve is (8) of:
72
72
seed:: UInt
73
73
end
74
74
75
- function Stress (; dim= 2 , Ptype= Float64, iterations= :auto , abstols= (√ (eps (Float64))),
76
- reltols= (√ (eps (Float64))), abstolx= (√ (eps (Float64))), weights= Array {Float64} (undef, 0 , 0 ),
77
- initialpos= Point{dim,Ptype}[], seed= 1 )
75
+ function Stress (; dim= 2 ,
76
+ Ptype= Float64,
77
+ iterations= :auto ,
78
+ abstols= 0.0 ,
79
+ reltols= 10e-6 ,
80
+ abstolx= 10e-6 ,
81
+ weights= Array {Float64} (undef, 0 , 0 ),
82
+ initialpos= Point{dim,Ptype}[],
83
+ seed= 1 )
78
84
if ! isempty (initialpos)
79
85
initialpos = Point .(initialpos)
80
86
Ptype = eltype (eltype (initialpos))
@@ -86,13 +92,6 @@ function Stress(; dim=2, Ptype=Float64, iterations=:auto, abstols=(√(eps(Float
86
92
Stress {dim,Ptype,IT,FT,WT} (iterations, abstols, reltols, abstolx, weights, initialpos, seed)
87
93
end
88
94
89
- function initialweights (D, T):: SparseMatrixCSC{T,Int64}
90
- map (D) do d
91
- x = T (d^ (- 2.0 ))
92
- return isfinite (x) ? x : zero (T)
93
- end
94
- end
95
-
96
95
function Base. iterate (iter:: LayoutIterator{<:Stress{Dim,Ptype,IT,FT}} ) where {Dim,Ptype,IT,FT}
97
96
algo, δ = iter. algorithm, iter. adj_matrix
98
97
N = size (δ, 1 )
@@ -109,45 +108,46 @@ function Base.iterate(iter::LayoutIterator{<:Stress{Dim,Ptype,IT,FT}}) where {Di
109
108
end
110
109
111
110
# calculate iteration if :auto
112
- maxiter = algo. iterations === :auto ? 400 * size (δ, 1 ) ^ 2 : algo. iterations
111
+ maxiter = algo. iterations === :auto ? 400 * N ^ 2 : algo. iterations
113
112
@assert maxiter > 0 " Iterations need to be > 0"
114
113
115
114
# if user provided weights not empty try those
116
- weights = isempty (algo. weights) ? initialweights (δ, FT) : algo. weights
115
+ make_symmetric! (δ)
116
+ distances = pairwise_distance (δ, FT)
117
+ weights = isempty (algo. weights) ? distances .^ (- 2 ) : algo. weights
117
118
118
119
@assert length (startpos) == size (δ, 1 ) == size (δ, 2 ) == size (weights, 1 ) == size (weights, 2 ) " Wrong size of weights?"
119
120
120
121
Lw = weightedlaplacian (weights)
121
122
pinvLw = pinv (Lw)
122
- s = stress (startpos, δ , weights)
123
+ oldstress = stress (startpos, distances , weights)
123
124
124
- # the `state` of the iterator is (#iter, old stress, old pos, weights, pinvLw, stopflag)
125
- return startpos, (1 , s , startpos, weights, pinvLw, maxiter, false )
125
+ # the `state` of the iterator is (#iter, old stress, old pos, weights, distances pinvLw, stopflag)
126
+ return startpos, (1 , oldstress , startpos, weights, distances , pinvLw, maxiter, false )
126
127
end
127
128
128
129
function Base. iterate (iter:: LayoutIterator{<:Stress{Dim,Ptype}} , state) where {Dim,Ptype}
129
130
algo, δ = iter. algorithm, iter. adj_matrix
130
- i, oldstress, oldpos, weights, pinvLw, maxiter, stopflag = state
131
- # newstress, oldstress, X0, i = state
131
+ i, oldstress, oldpos, weights, distances, pinvLw, maxiter, stopflag = state
132
132
133
133
if i >= maxiter || stopflag
134
134
return nothing
135
135
end
136
136
137
137
# TODO the faster way is to drop the first row and col from the iteration
138
- t = LZ (oldpos, δ , weights)
138
+ t = LZ (oldpos, distances , weights)
139
139
positions = similar (oldpos) # allocate new array but keep type of oldpos
140
140
mul! (positions, pinvLw, (t * oldpos))
141
141
@assert all (x -> all (map (isfinite, x)), positions)
142
- newstress = stress (positions, δ , weights)
142
+ newstress = stress (positions, distances , weights)
143
143
144
144
if abs (newstress - oldstress) < algo. reltols * newstress ||
145
145
abs (newstress - oldstress) < algo. abstols ||
146
146
norm (positions - oldpos) < algo. abstolx
147
147
stopflag = true
148
148
end
149
149
150
- return positions, (i + 1 , newstress, positions, weights, pinvLw, maxiter, stopflag)
150
+ return positions, (i + 1 , newstress, positions, weights, distances, pinvLw, maxiter, stopflag)
151
151
end
152
152
153
153
"""
@@ -160,8 +160,7 @@ Input:
160
160
161
161
See (1) of Reference
162
162
"""
163
- function stress (positions:: AbstractArray{Point{T,N}} , d= ones (T, length (positions), length (positions)),
164
- weights= initialweights (d, T)) where {T,N}
163
+ function stress (positions:: AbstractArray{Point{T,N}} , d, weights) where {T,N}
165
164
s = zero (T)
166
165
n = length (positions)
167
166
@assert n == size (d, 1 ) == size (d, 2 ) == size (weights, 1 ) == size (weights, 2 )
196
195
Computes L^Z defined in (5) of the Reference
197
196
198
197
Input: Z: current layout (coordinates)
199
- d: Ideal distances (default: all 1)
200
- weights: weights (default: d.^-2)
198
+ d: Ideal distances
199
+ weights: weights
201
200
"""
202
201
function LZ (Z:: AbstractVector{Point{N,T}} , d, weights) where {N,T}
203
202
n = length (Z)
@@ -217,3 +216,32 @@ function LZ(Z::AbstractVector{Point{N,T}}, d, weights) where {N,T}
217
216
end
218
217
return L
219
218
end
219
+
220
+ """
221
+ pairwise_distance(δ)
222
+
223
+ Calculate the pairwise distances of a the graph from a adjacency matrix
224
+ using the Floyd-Warshall algorithm.
225
+
226
+ https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
227
+ """
228
+ function pairwise_distance (δ, :: Type{T} = Float64) where {T}
229
+ N = size (δ, 1 )
230
+ d = Matrix {T} (undef, N, N)
231
+ @inbounds for j in 1 : N, i in 1 : N
232
+ if i == j
233
+ d[i, j] = zero (eltype (d))
234
+ elseif iszero (δ[i, j])
235
+ d[i, j] = typemax (eltype (d))
236
+ else
237
+ d[i, j] = δ[i, j]
238
+ end
239
+ end
240
+
241
+ @inbounds for k in 1 : N, i in 1 : N, j in 1 : N
242
+ if d[i, k] + d[k, j] < d[i, j]
243
+ d[i, j] = d[i, k] + d[k, j]
244
+ end
245
+ end
246
+ return d
247
+ end
0 commit comments