Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
977cad5
some refactoring & fixing a bug in spectral_layout
simonschoelly Dec 23, 2018
23f73a3
Merge pull request #86 from matbesancon/project-toml
simonschoelly Apr 22, 2019
d8f224c
Curve linetype
CiaranOMara Jun 21, 2019
8ca1552
Use vector nodesize
CiaranOMara Jun 21, 2019
55541cc
Use jth location and nodesize
CiaranOMara Jun 21, 2019
ccefb95
Remove multiplier
CiaranOMara Jun 21, 2019
08339a3
Remove duplication
CiaranOMara Jun 21, 2019
28559e6
Simplify arrow coordinates
CiaranOMara Jun 21, 2019
0d2c328
Make exit angle relative to node centres
CiaranOMara Jun 21, 2019
5edfece
Remove duplicate computation
CiaranOMara Jun 21, 2019
9df0e27
Preserve style
CiaranOMara Jun 21, 2019
a96c663
Basic implementation of self directed loops
CiaranOMara Jun 21, 2019
c1d5ef9
Save comparisons
CiaranOMara Jun 22, 2019
480f568
Use safer hypot function
CiaranOMara Jun 23, 2019
886945e
Remove unused distance calculations
CiaranOMara Jun 24, 2019
31a69e3
Update Project.toml compat section
CiaranOMara Jun 24, 2019
972d7fa
Update curve type
CiaranOMara Jun 24, 2019
bf20ed0
Add karate_edges back
simonschoelly Jun 24, 2019
ad20f2e
Merge branch 'master' of git://github.com/JuliaGraphs/GraphPlot.jl in…
simonschoelly Jun 24, 2019
32ec888
Use SimpleGraph instead of smallgraph
simonschoelly Jun 25, 2019
e534175
Remove VisualRegresionTests from [compat]
simonschoelly Jun 25, 2019
d85cb80
Try more liberal Compat requirement
CiaranOMara Jun 25, 2019
1027506
Add VisualRegressionTests back to Project.toml
simonschoelly Jun 26, 2019
92dcc35
Remove splats and slurps
CiaranOMara Jun 26, 2019
6458908
Merge pull request #80 from simonschoelly/fruchterman_reingold
simonschoelly Jun 28, 2019
dea58c9
Merge branch 'master' into hotfix/curve
simonschoelly Jun 28, 2019
534a9e6
Merge pull request #90 from CiaranOMara/hotfix/curve
simonschoelly Jul 12, 2019
3de1d0b
Update Project.toml
matbesancon Jan 18, 2020
877feee
remove method ambiguity
matbesancon Jan 18, 2020
8bb16c3
remove travis 07
matbesancon Feb 7, 2020
819aff8
Use smallgraph instead of graphfamous
simonschoelly Mar 8, 2020
0ec9af4
Use LightGraphs instead of Graphs in README.md
simonschoelly Mar 8, 2020
282a910
Update outdated code for Vector creation
simonschoelly Mar 8, 2020
d390f28
Explicitly import Cairo for drawing to file
simonschoelly Mar 8, 2020
4ea394e
Update outdated examples notebook
simonschoelly Mar 8, 2020
4acffe7
Merge pull request #104 from simonschoelly/update-readme
simonschoelly Mar 8, 2020
59cc5e0
Merge pull request #105 from simonschoelly/update-outdated-examples-n…
simonschoelly Mar 8, 2020
2f117eb
Request Compose compat to include 0.8 please (#103)
dehann Apr 1, 2020
74897b8
bump version for new Compose compat
Apr 4, 2020
a15382b
Merge pull request #106 from KristofferC/patch-1
simonschoelly Apr 5, 2020
4906f28
Delete the `test/REQUIRE` file
DilumAluthge Apr 8, 2020
d18be2e
Merge pull request #108 from aluthge-forks/dpa/test/require
matbesancon Apr 8, 2020
beccfed
Update versions of dependencies
bkamins May 10, 2020
e5d5732
Update Project.toml
bkamins May 10, 2020
1b8c963
Merge pull request #109 from bkamins/patch-1
matbesancon May 10, 2020
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
*.jl.mem
*.svg
Manifest.toml
test/data/*-comparison.png
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ os:
- linux
- osx
julia:
- 0.7
- 1.0
- nightly

Expand Down
16 changes: 10 additions & 6 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "GraphPlot"
uuid = "a2cc645c-3eea-5389-862e-a155d0052231"
authors = ["JuliaGraphs"]
version = "0.4.0"
version = "0.4.2"

[deps]
ArnoldiMethod = "ec485272-7323-5ecc-a04f-4719b315124d"
Expand All @@ -14,6 +14,15 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[compat]
ArnoldiMethod = "0.0.4"
ColorTypes = "0.9, 0.10"
Colors = "0.11, 0.12"
Compose = "0.7, 0.8"
LightGraphs = "1.1"
VisualRegressionTests = "0.2, 0.3"
julia = "1"

[extras]
Cairo = "159f3aea-2a34-519c-b102-8c37f9878175"
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
Expand All @@ -22,8 +31,3 @@ VisualRegressionTests = "34922c18-7c2a-561c-bac1-01e79b2c4c92"

[targets]
test = ["Test", "Cairo", "ImageMagick", "VisualRegressionTests"]

[compat]
"Compose" = "0.7"
"LightGraphs" = "1.1"
"VisualRegressionTests" = "0.2"
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,16 @@ using GraphPlot
# Usage
## karate network
```julia
g = graphfamous("karate")
using LightGraphs: smallgraph
g = smallgraph(:karate)
gplot(g)

```

## Add node label
```julia
using Graphs
nodelabel = [1:num_vertices(g)]
using LightGraphs
nodelabel = [1:nv(g)]
gplot(g, nodelabel=nodelabel)

```
Expand All @@ -46,7 +47,7 @@ gplot(g, nodelabel=nodelabel, nodelabeldist=1.5, nodelabelangleoffset=π/4)
## Control the node size
```julia
# nodes size proportional to their degree
nodesize = [Graphs.out_degree(v, g) for v in Graphs.vertices(g)]
nodesize = [LightGraphs.out_degree(g, v) for v in LightGraphs.vertices(g)]
gplot(g, nodesize=nodesize)
```

Expand All @@ -56,7 +57,7 @@ Feed the keyword argument `nodefillc` a color array, ensure each node has a colo
using Colors

# Generate n maximally distinguishable colors in LCHab space.
nodefillc = distinguishable_colors(num_vertices(g), colorant"blue")
nodefillc = distinguishable_colors(nv(g), colorant"blue")
gplot(g, nodefillc=nodefillc, nodelabel=nodelabel, nodelabeldist=1.8, nodelabelangleoffset=π/4)
```

Expand All @@ -75,13 +76,13 @@ gplot(g, nodelabelsize=nodelabelsize, nodesize=nodesize, nodelabel=nodelabel)

## Draw edge labels
```julia
edgelabel = [1:Graphs.num_edges(g)]
edgelabel = [1:LightGraphs.ne(g)]
gplot(g, edgelabel=edgelabel, nodelabel=nodelabel)
```

## Adjust edge labels
```julia
edgelabel = [1:Graphs.num_edges(g)]
edgelabel = [1:LightGraphs.ne(g)]
gplot(g, edgelabel=edgelabel, nodelabel=nodelabel, edgelabeldistx=0.5, edgelabeldisty=0.5)
```

Expand Down Expand Up @@ -119,9 +120,9 @@ gplot(g, layout=spectral_layout)
```
### shell layout
```julia
nlist = Array(Vector{Int}, 2) # two shells
nlist = Vector{Vector{Int}}(undef, 2) # two shells
nlist[1] = [1:5] # first shell
nlist[2] = [6:num_vertices(g)] # second shell
nlist[2] = [6:nv(g)] # second shell
locs_x, locs_y = shell_layout(g, nlist)
gplot(g, locs_x, locs_y, nodelabel=nodelabel)
```
Expand All @@ -133,7 +134,7 @@ gplot(g, linetype="curve")

## Save to figure
```{execute="false"}
using Compose
using Cairo, Compose
# save to pdf
draw(PDF("karate.pdf", 16cm, 16cm), gplot(g))
# save to png
Expand Down
12,956 changes: 9,818 additions & 3,138 deletions examples/graphplot.ipynb

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions src/collapse_plot.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using GraphPlot

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

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

collapsed_graph, collapsed_weights
return collapsed_graph, collapsed_weights
end

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

function collapse_layout(g::AbstractGraph{T}, membership::Vector{Int}) where {T<:Integer}
function collapse_layout(g::AbstractGraph, membership::Vector{Int})
lightg = LightGraphs.SimpleGraph(nv(g))
for e in edges(g)
u = src(e)
Expand Down Expand Up @@ -92,5 +92,5 @@ function collapse_layout(g::AbstractGraph{T}, membership::Vector{Int}) where {T<
ly[node] = 1.8*length(nodes)/N*subly[idx] + cly[lbl]
end
end
lx, ly
return lx, ly
end
98 changes: 51 additions & 47 deletions src/layout.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using SparseArrays: SparseMatrixCSC, sparse
using ArnoldiMethod: LR
using ArnoldiMethod: SR
using Base: OneTo
using LinearAlgebra: eigen

"""
Expand Down Expand Up @@ -39,7 +40,7 @@ Position nodes on a circle.

**Parameters**

*G*
*g*
a graph

**Returns**
Expand All @@ -51,7 +52,7 @@ but will be normalized and centered anyway
**Examples**

```
julia> g = simple_house_graph()
julia> g = smallgraph(:house)
julia> locs_x, locs_y = circular_layout(g)
```
"""
Expand All @@ -60,18 +61,18 @@ function circular_layout(g)
return [0.0], [0.0]
else
# Discard the extra angle since it matches 0 radians.
θ = range(0, stop=2pi, length=_nv(G)+1)[1:end-1]
θ = range(0, stop=2pi, length=nv(g)+1)[1:end-1]
return cos.(θ), sin.(θ)
end
end

"""
This function is copy from [IainNZ](https://github.com/IainNZ)'s [GraphLayout.jl](https://github.com/IainNZ/GraphLayout.jl)

Use the spring/repulsion model of Fruchterman and Reingold (1991):
Use a modified version of the spring/repulsion model of Fruchterman and Reingold (1991):

+ Attractive force: f_a(d) = d^2 / k
+ Repulsive force: f_r(d) = -k^2 / d
+ Attractive force: f_a(d) = d / k
+ Repulsive force: f_r(d) = -k^2 / d^2

where d is distance between two vertices and the optimal distance
between vertices k is defined as C * sqrt( area / num_vertices )
Expand All @@ -96,63 +97,66 @@ Integer seed for pseudorandom generation of locations (default = 0).

**Examples**
```
julia> g = graphfamous("karate")
julia> g = smallgraph(:karate)
julia> locs_x, locs_y = spring_layout(g)
```
"""
function spring_layout(g::AbstractGraph{T}, locs_x, locs_y; C=2.0, MAXITER=100, INITTEMP=2.0) where {T<:Integer}

#size(adj_matrix, 1) != size(adj_matrix, 2) && error("Adj. matrix must be square.")
N = nv(g)
function spring_layout(g::AbstractGraph,
locs_x=2*rand(nv(g)).-1.0,
locs_y=2*rand(nv(g)).-1.0;
C=2.0,
MAXITER=100,
INITTEMP=2.0)

nvg = nv(g)
adj_matrix = adjacency_matrix(g)

# The optimal distance bewteen vertices
K = C * sqrt(4.0 / N)
k = C * sqrt(4.0 / nvg)
k² = k * k

# Store forces and apply at end of iteration all at once
force_x = zeros(N)
force_y = zeros(N)
force_x = zeros(nvg)
force_y = zeros(nvg)

# Iterate MAXITER times
@inbounds for iter = 1:MAXITER
# Calculate forces
for i = 1:N
for i = 1:nvg
force_vec_x = 0.0
force_vec_y = 0.0
for j = 1:N
for j = 1:nvg
i == j && continue
d_x = locs_x[j] - locs_x[i]
d_y = locs_y[j] - locs_y[i]
d = sqrt(d_x^2 + d_y^2)
if adj_matrix[i,j] != zero(eltype(adj_matrix)) || adj_matrix[j,i] != zero(eltype(adj_matrix))
# F = d^2 / K - K^2 / d
F_d = d / K - K^2 / d^2
dist² = (d_x * d_x) + (d_y * d_y)
dist = sqrt(dist²)

if !( iszero(adj_matrix[i,j]) && iszero(adj_matrix[j,i]) )
# Attractive + repulsive force
# F_d = dist² / k - k² / dist # original FR algorithm
F_d = dist / k - k² / dist²
else
# Just repulsive
# F = -K^2 / d^
F_d = -K^2 / d^2
# F_d = - / dist # original FR algorithm
F_d = - / dist²
end
# d / sin θ = d_y/d = fy/F
# F /| dy fy -> fy = F*d_y/d
# / | cos θ = d_x/d = fx/F
# /--- -> fx = F*d_x/d
# dx fx
force_vec_x += F_d*d_x
force_vec_y += F_d*d_y
end
force_x[i] = force_vec_x
force_y[i] = force_vec_y
end
# Cool down
TEMP = INITTEMP / iter
temp = INITTEMP / iter
# Now apply them, but limit to temperature
for i = 1:N
force_mag = sqrt(force_x[i]^2 + force_y[i]^2)
scale = min(force_mag, TEMP)/force_mag
for i = 1:nvg
fx = force_x[i]
fy = force_y[i]
force_mag = sqrt((fx * fx) + (fy * fy))
scale = min(force_mag, temp) / force_mag
locs_x[i] += force_x[i] * scale
#locs_x[i] = max(-1.0, min(locs_x[i], +1.0))
locs_y[i] += force_y[i] * scale
#locs_y[i] = max(-1.0, min(locs_y[i], +1.0))
end
end

Expand All @@ -165,11 +169,12 @@ function spring_layout(g::AbstractGraph{T}, locs_x, locs_y; C=2.0, MAXITER=100,
map!(z -> scaler(z, min_x, max_x), locs_x, locs_x)
map!(z -> scaler(z, min_y, max_y), locs_y, locs_y)

return locs_x,locs_y
return locs_x, locs_y
end

using Random: MersenneTwister
function spring_layout(g::AbstractGraph; seed::Integer=0, kws...)

function spring_layout(g::AbstractGraph, seed::Integer, kws...)
rng = MersenneTwister(seed)
spring_layout(g, 2 .* rand(rng, nv(g)) .- 1.0, 2 .* rand(rng,nv(g)) .- 1.0; kws...)
end
Expand All @@ -181,28 +186,27 @@ Position nodes in concentric circles.

**Parameters**

*G*
*g*
a graph

*nlist*
Vector of Vector, Vector of node Vector for each shell.

**Examples**
```
julia> g = graphfamous("karate")
julia> g = smallgraph(:karate)
julia> nlist = Array{Vector{Int}}(2)
julia> nlist[1] = [1:5]
julia> nlist[2] = [6:num_vertiecs(g)]
julia> locs_x, locs_y = shell_layout(g, nlist)
```
"""
function shell_layout(G, nlist::Union{Nothing, Vector{Vector{Int}}} = nothing)
if _nv(G) == 1
function shell_layout(g, nlist::Union{Nothing, Vector{Vector{Int}}} = nothing)
if nv(g) == 1
return [0.0], [0.0]
end
if nlist == nothing
nlist = Array{Vector{Int}}(1)
nlist[1] = collect(1:_nv(G))
nlist = [collect(1:nv(g))]
end
radius = 0.0
if length(nlist[1]) > 1
Expand All @@ -217,7 +221,7 @@ function shell_layout(G, nlist::Union{Nothing, Vector{Vector{Int}}} = nothing)
append!(locs_y, radius*sin.(θ))
radius += 1.0
end
locs_x, locs_y
return locs_x, locs_y
end

"""
Expand All @@ -237,20 +241,20 @@ the edge weight. If None, then all edge weights are 1.

**Examples**
```
julia> g = graphfamous("karate")
julia> g = smallgraph(:karate)
julia> weight = rand(num_edges(g))
julia> locs_x, locs_y = spectral_layout(g, weight)
```
"""
function spectral_layout(g::AbstractGraph{T}, weight=nothing) where {T<:Integer}
function spectral_layout(g::AbstractGraph, weight=nothing)
if nv(g) == 1
return [0.0], [0.0]
elseif nv(g) == 2
return [0.0, 1.0], [0.0, 0.0]
end

if weight == nothing
weight = ones(length(edges(g)))
weight = ones(ne(g))
end
if nv(g) > 500
A = sparse(Int[src(e) for e in edges(g)],
Expand All @@ -276,7 +280,7 @@ function _spectral(A::SparseMatrixCSC)
data = vec(sum(A, dims=1))
D = sparse(Base.OneTo(length(data)), Base.OneTo(length(data)), data)
L = D - A
eigenvalues, eigenvectors = LightGraphs.LinAlg.eigs(L, nev=3, which=LR())
eigenvalues, eigenvectors = LightGraphs.LinAlg.eigs(L, nev=3, which=SR())
index = sortperm(real(eigenvalues))[2:3]
return real(eigenvectors[:, index[1]]), real(eigenvectors[:, index[2]])
end
Loading