Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Springify (modded Spring layout with CUDA kernel) #51

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/NetworkLayout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ end
include("sfdp.jl")
include("buchheim.jl")
include("spring.jl")
include("springify.jl")
include("stress.jl")
include("spectral.jl")
include("shell.jl")
Expand Down
104 changes: 104 additions & 0 deletions src/springify.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
export Springify, springify

using CUDA
mv2cuda(arr::CuArray) = arr
mv2cuda(arr) = CuArray(arr)

"""
Springify(; kwargs...)(adj_matrix)
springify(adj_matrix; kwargs...)

Modification of the `Spring` algorithm. It does a little bit more separation.

- Attractive force: `f_a(d) = gamma * d₁₂ * d`
- Repulsive force: `f_r(d) = -alpha * d₁₂ / d`

where `d` is distance between two vertices, d₁₂ is the x or y axis relative
distance of the vertices compared to `d`

Takes adjacency matrix representation of a network and returns coordinates of
the nodes.

## Keyword Arguments
- `iterations=100`: number of iterations
- `alpha=0.1f0`: Repulsive force constant
- `gamma=0.01f0`: Attractive force constant
- `initialpos=Point{2,Float32}[]`

Provide list of initial positions. If length does not match Network size the
initial positions will be dropped and inited with random values between [-1,1]
in every coordinate.
"""

@addcall mutable struct Springify <: AbstractLayout{2,Float32}
iterations::Int
alpha::Float32
gamma::Float32
initialpos::Vector{Point{2,Float32}}
end

function Springify(; alpha=0.1f0, gamma=0.01f0, iterations=200, initialpos=Point{2,Float32}[])
return Springify(iterations, alpha, gamma, initialpos)
end


function NetworkLayout.layout(algo::Springify, adj_matrix::AbstractMatrix)
N = assertsquare(adj_matrix)

force = Array{Float32,2}(undef,N,2) |> mv2cuda
if N != length(algo.initialpos)
coords = randn(Float32,N,2)
else
coords = Array{Float32,2}(undef,N,2)
for (i,xy) in enumerate(algo.initialpos)
coords[i,1] = xy[1]
coords[i,2] = xy[2]
end
end
# coordc .= reshape(vcat([pts[1] for pts in plt[:node_pos][]]...,[pts[2] for pts in plt[:node_pos][]]...),N,2)
coord_gpu = coords |> mv2cuda
adj_m = Array(adj_matrix) |> mv2cuda
alpha = algo.alpha
gamma = algo.gamma
nth = 128
for _ in 1:algo.iterations
@cuda threads=nth blocks=cld(N, nth) pull_and_push_forces(adj_m,coord_gpu,force,alpha, gamma,N)
coord_gpu.+=force
end
res_coord = Array(coord_gpu)

if !isempty(algo.initialpos)
for i in 1:size(res_coord,1)
algo.initialpos[i] = Point{2,Float32}(res_coord[i,1], res_coord[i,2])
end
else
algo.initialpos = [Point{2,Float32}(res_coord[i,1], res_coord[i,2]) for i in 1:size(res_coord,1)]
end
return algo.initialpos
end

@inline has_edge(mat,I,i) = @inbounds (mat[i,I] == 1 || mat[I,i] == 1)
pull_and_push_forces(adj_m,coord,force,alpha, gamma,N) = @inbounds begin
I = (blockIdx().x - 1) * blockDim().x + threadIdx().x
if I > N; return end
force[I,1] = 0f0
force[I,2] = 0f0
for i in 1:I-1
isconn = has_edge(adj_m,i,I)
calc_forces(isconn,coord,force, alpha, gamma, i, I, 1)
calc_forces(isconn,coord,force, alpha, gamma, i, I, 2)
end
for i in I+1:N
isconn = has_edge(adj_m,i,I)
calc_forces(isconn,coord,force, alpha, gamma, i, I, 1)
calc_forces(isconn,coord,force, alpha, gamma, i, I, 2)
end
nothing
end
@inline calc_forces(isconn, coord,force, alpha, gamma, i, I, xy) = @inbounds begin
dd = sqrt((coord[i,1] - coord[I,1])^2 + (coord[i,2] - coord[I,2])^2)
dx = coord[i,xy] - coord[I,xy]
e=dx/dd
force[I,xy] -= alpha / (1f-6 + dd)*e
force[I,xy] += isconn==1 ? gamma*dd*e : 0f0 # -alpha / (1f-6 + dd*dd)*e # - dx*(1/dd)*1000.1f0
end
13 changes: 13 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,19 @@ jagmesh_adj = jagmesh()
end
end

@testset "Testing Springfy Algorithm" begin
println("Springfy wheel_graph")

@testset "Testing wheel_graph" begin
g = wheel_graph(10)
adj_matrix = adjacency_matrix(g)
positions = @time Springify(; iterations=100)(adj_matrix)
@test typeof(positions) == Vector{Point2f}
# @test positions == springify(adj_matrix; iterations=100) # it starts from random coords... so it will fail (I guess this is why you introduced seed, but I don't want to bother it with it now)
end

end

@testset "Testing Spectral Algorithm" begin
println("Spectral wheel_graph")
@testset "Testing wheel_graph" begin
Expand Down