Comments (4)
It seems like what they call :stress
refers to another function by_axis_local_stress_graph
which is not taken from NetworkLayout.
I am not sure how those two relate as I am not familiar with the inner workings of the Stress layout. But they reference "section 2.3 from http://link.springer.com/chapter/10.1007%2F978-3-540-31843-9_25#page-1" as a source. It would be cool to add that to NetworkLayout.
from graphmakie.jl.
In case you're interested: here is a quick&dirty port of those functions for GraphMakie.
using Pkg
Pkg.activate(temp=true)
Pkg.add("GLMakie")
Pkg.add("LightGraphs")
Pkg.add("GraphMakie")
using GLMakie
using LightGraphs
using GraphMakie
using GraphMakie.NetworkLayout
using SparseArrays
adj_matrix = [0 1 1 0 0 0 0 0 0 0;
0 0 0 0 1 1 0 0 0 0;
0 0 0 1 0 0 1 0 1 0;
0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 1 0 1;
0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0;
0 0 0 0 0 0 0 0 0 0]
g = SimpleGraph(SimpleDiGraph(adj_matrix))
graphplot(g; layout=Stress())
# code copied and adapted from GraphRecipes
# https://github.com/JuliaPlots/GraphRecipes.jl/blob/5559436a981089eb842f4c7b7a96a4dbf9abe466/src/graph_layouts.jl#L157-L204
function by_axis_local_stress_graph(g)
adjmat = adjacency_matrix(g)
# parameters
node_weights = ones(size(adjmat,1))
dim = 2
free_dims = 1:dim
x = rand(length(node_weights))
y = rand(length(node_weights))
z = rand(length(node_weights))
maxiter = 1000
n = length(node_weights)
# graph-theoretical distance between node i and j (i.e. shortest path distance)
# TODO: calculate a real distance
dist = estimate_distance(adjmat)
# @show dist
# also known as kᵢⱼ in "axis-by-axis stress minimization". the -2 could also be 0 or -1?
w = dist .^ -2
# in each iteration, we update one dimension/node at a time, reducing the total stress with each update
X = dim == 2 ? (x, y) : (x, y, z)
laststress = stressf(X, dist, w)
for k in 1:maxiter
for p in free_dims
for i=1:n
numer, denom = 0.0, 0.0
for j=1:n
i==j && continue
numer += w[i,j] * (X[p][j] + dist[i,j] * (X[p][i] - X[p][j]) / norm_ij(X, i, j))
denom += w[i,j]
end
if denom != 0
X[p][i] = numer / denom
end
end
end
# check for convergence of the total stress
thisstress = stressf(X, dist, w)
if abs(thisstress - laststress) / abs(laststress) < 1e-6
# info("converged. numiter=$k last=$laststress this=$thisstress")
break
end
laststress = thisstress
end
GraphMakie.Pointf.(zip(X...))
end
function estimate_distance(adjmat::AbstractMatrix)
g = LightGraphs.Graph(adjmat)
dists = convert(Matrix{Float64}, hcat(map(i->LightGraphs.dijkstra_shortest_paths(g, i).dists, LightGraphs.vertices(g))...))
tot = 0.0; cnt = 0
for (i,d) in enumerate(dists)
if d < 1e10
tot += d
cnt += 1
end
end
avg = cnt > 0 ? tot / cnt : 1.0
for (i,d) in enumerate(dists)
if d > 1e10
dists[i] = 3avg
end
end
dists
end
norm_ij(X, i, j) = sqrt(sum(Float64[(v[i]-v[j])^2 for v in X]))
stressf(X, dist, w, i, j) = w[i,j] * (norm_ij(X, i, j) - dist[i,j])^2
function stressf(X, dist, w)
tot = 0.0
for i=1:size(X,1), j=1:i-1
tot += stressf(X, dist, w, i, j)
end
tot
end
graphplot(g; layout=by_axis_local_stress_graph)
from graphmakie.jl.
@hexaeder thanks for the explanations. Indeed, it would be really nice to have this network layout integrated into NetwrokLayout.jl so that it can be used from GraphMakie.jl. So far, it has been the one that works best for all my graphs. Thanks also for the "quick&dirty" port. I'll use this for now. I'll change the title to a feat request.
from graphmakie.jl.
I figured it was better to move this to NetworkLayout.jl JuliaGraphs/NetworkLayout.jl#39. I'll close this for now.
from graphmakie.jl.
Related Issues (20)
- curve_distance & curve_distance_usage observable not updating figure
- Precompilation stuck on Julia 1.9 HOT 6
- handle overlapping `nlabels` HOT 6
- node_color and node_marker only accept Symbol HOT 5
- Performant graph plotting and benchmarking HOT 4
- Element-wise `automatic` configuration HOT 1
- Deleting a plot doesn't remove all listeners HOT 2
- Mixing directed and undirected edges HOT 2
- Add padding around graphplot to accommodate node size. HOT 2
- `node_attr` broken using Makie v0.19.11 HOT 1
- Slow start HOT 2
- Setting node size with ilabel
- Improve Reftests HOT 1
- provide convenience function for MetaGraphsNext HOT 7
- KeyError: key :textsize not found when running doc examples HOT 2
- UndefVarError: `mouse_selection` not defined HOT 5
- arrow_shift = :end works poorly with larger edge width HOT 6
- bounding box cuts off nodes HOT 3
- `pick` one `LineSegments` returns odd index HOT 2
- Dynamic Layout + Interaction. HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphmakie.jl.