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

Kamada kawai #89

Open
wants to merge 9 commits 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
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
Compose = "a81c6b42-2e10-5240-aca2-a61377ecd94b"
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

Expand Down
2 changes: 2 additions & 0 deletions REQUIRE
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ Colors
ColorTypes
Compose 0.7.0
LightGraphs 1.1.0
Optim
Distances
103 changes: 103 additions & 0 deletions src/KamadaKawai.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import Optim
using Distances
@doc raw"""
Computes the layout corresponding to

```math
\min \sum_{i,j} (K_{ij} - \|x_i - x_j\|)^\frac{1}{2},
```
where $K_{ij}$ is the shortest path distance between vertices `i` abd `j`, and $x_i$ and $x_j$ are the positions of vertices `i` and `j` in the layout, respectively.

Inputs:

- G: Graph to layout
- X: starting positions. If no starting positions are provided a shell_layout is constructed.

Optional keyword arguments:

- maxiter: maximum number of iterations Optim.optimize will perform in an attempt to optimize the layout.
- distmx: Distance matrix for the Floyd-Warshall algorithm to discern shortest path distances on the graph. By default the edge weights of the graph.

Outputs:

- (`locs_x`, `locs_y`) positions in the x and y directions, respectively.

"""
function kamada_kawai_layout(G, X=nothing; maxiter=100, distmx=weights(G) )
if !is_connected(G)
@warn "This graph is disconnected. Results may not be reasonable."
end
if X===nothing
locs_x = zeros(nv(G))
locs_y = zeros(nv(G))
else
locs_x = X[1]
locs_y = X[2]
end

function Objective(M,K)
D = pairwise(Euclidean(),M, dims=2)
D-= K
R = sum(D.^2)/2
return R
end

function dObjective!(dR,M,K)
dR .= zeros(size(M))
Vs = size(M,2)
D = pairwise(Euclidean(),M, dims=2)
D += I # Prevent division by zero
D .= K./D # Use negative for simplicity, since diag K = 0 everything is fine.
D .-= 1.0 # (K-(D+I))./(D+I) = K./(D+I) .- 1.0
D += I # Remove the false diagonal
for v1 in 1:Vs
dR[:,v1] .= -M[:,v1]*sum(D[:,v1])
end
dR .+= M*D
dR .*=2
return dR
end

function scaler(z, a, b)
2.0*((z - a)/(b - a)) - 1.0
end
N = nv(G)
Vertices=collect(vertices(G))

if X !== nothing
_locs_x = locs_x
_locs_y = locs_y
else
Vmax=findmax([degree(G,x) for x in vertices(G)])[2]
filter!(!isequal(Vmax), Vertices)
Shells=[[Vmax]]
VComplement = copy(Shells[1])
while !isempty(Vertices)
Interim = filter(!∈(VComplement),vcat([collect(neighbors(G,s)) for s in Shells[end]]...))
unique!(Interim)
push!(Shells,Interim)
filter!(!∈(Shells[end]),Vertices)
append!(VComplement,Shells[end])
end
_locs_x, _locs_y = shell_layout(G,Shells)
end

# The optimal distance between vertices
# Currently only LightGraphs are supported using the Dijkstra shortest path algorithm

K = floyd_warshall_shortest_paths(G,distmx).dists

M0 = vcat(_locs_x',_locs_y')
OptResult = Optim.optimize(x->Objective(x,K),(x,y) -> dObjective!(x,y,K), M0, method=Optim.LBFGS(), iterations = maxiter )
M0 = Optim.minimizer(OptResult)

(min_x, max_x), (min_y, max_y) = extrema(M0,dims=2)
locs_x .= M0[1,:]
locs_y .= M0[2,:]

# Scale to unit square
map!(z -> scaler(z, min_x, max_x), locs_x, locs_x)
map!(z -> scaler(z, min_y, max_y), locs_y, locs_y)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably also some function that we can generalize eventually (also does not have to be in this PR)


return locs_x,locs_y
end
4 changes: 3 additions & 1 deletion src/layout.jl
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ Vector of Vector, Vector of node Vector for each shell.
julia> g = smallgraph(:karate)
julia> nlist = Array{Vector{Int}}(2)
julia> nlist[1] = [1:5]
julia> nlist[2] = [6:num_vertiecs(g)]
julia> nlist[2] = [6:num_vertices(g)]
julia> locs_x, locs_y = shell_layout(g, nlist)
```
"""
Expand Down Expand Up @@ -284,3 +284,5 @@ function _spectral(A::SparseMatrixCSC)
index = sortperm(real(eigenvalues))[2:3]
return real(eigenvectors[:, index[1]]), real(eigenvectors[:, index[2]])
end

include("KamadaKawai.jl")