Skip to content

Commit 08694e8

Browse files
committed
remove layout from exports, add lowercase calls
- the layout export blocks identifier in global scope - new lowercase functions are easy to read for singe application i.e. `spring(g; kwargs...)` instead of `Spring(;kwargs)(g)`
1 parent 9c61bc4 commit 08694e8

File tree

11 files changed

+109
-36
lines changed

11 files changed

+109
-36
lines changed

docs/src/index.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,16 @@ callable and transforms the adjacency matrix and returns a list of `Point{N,T}`
2727
alg = LayoutAlgorithm(; p1="foo", p2=:bar)
2828
positions = alg(adj_matrix)
2929
```
30+
Each of the layouts comes with a lowercase function version:
31+
```
32+
positions = layoutalgorithm(adj_matrix; p1="foo", b2=:bar)
33+
```
34+
3035
Instead of using the adjacency matrix you can use `AbstractGraph` types from [`LightGraphs.jl`](https://github.com/JuliaGraphs/LightGraphs.jl) directly.
36+
```
37+
g = complete_graph(10)
38+
positions = layoutalgorithm(g)
39+
```
3140

3241
## Scalable Force Directed Placement
3342
```@docs

docs/src/interface.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,26 @@ Each layout is implemented as subtype of [`AbstractLayout`](@ref).
1515
AbstractLayout
1616
```
1717

18-
Therefore, each `Layout <: AbstractLayout` is a functor and can be passed around as a function `graph ↦ node_positions` which encapsulates all the parameters. This is handy for plotting libraries such as [GraphMakie.jl](http://juliaplots.org/GraphMakie.jl/previews/PR9/).
18+
Therefore, each `LayoutAlgorithm <: AbstractLayout` is a functor and can be passed around as a function `graph ↦ node_positions` which encapsulates all the parameters. This is handy for plotting libraries such as [GraphMakie.jl](http://juliaplots.org/GraphMakie.jl/previews/PR9/).
1919

2020
There are some additional guidelines:
2121
- All of the parameters should be keyword arguments, i.e. it should be allways
22-
possible to call `Layout()` without specifying any parameters.
22+
possible to call `LayoutAlgorithm()` without specifying any parameters.
2323
- Algorithms should allways return `Vector{Point{Dim,Ptype}}`. If the type or
2424
dimensions can be altered use the keywords `dim` and `Ptype` for it.
2525
- Some parameters may depend on the specific network (i.e. length of start
2626
positions vector). If possible, there should be a fallback option (i.e.
2727
truncate the list of start positions if network is to small or append with
2828
random values).
29+
30+
It is convenient to define the lowercase functions
31+
```
32+
layoutalgorithm(g; kwargs...) = layout(LayoutAlgorihtm(; kwargs...), g)
33+
```
34+
which can done using this macro:
35+
```@docs
36+
addcall
37+
```
2938

3039
## Iterative Layouts
3140
Iterative layouts are a specific type of layouts which produce a sequence of positions rather than a single list of positions. Those algorithms are implemented as subtypes of [`IterativeLayout`](@ref):

src/NetworkLayout.jl

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ using Requires
55
using LinearAlgebra: norm
66
using Random
77

8-
export LayoutIterator, layout
8+
export LayoutIterator
99

1010
"""
1111
AbstractLayout{Dim,Ptype}
@@ -86,7 +86,11 @@ function layout(alg::IterativeLayout, adj_matrix::AbstractMatrix)
8686
end
8787

8888
function __init__()
89-
@require LightGraphs="093fc24a-ae57-5d10-9952-331d41423f4d" layout(l::AbstractLayout, g::LightGraphs.AbstractGraph) = layout(l, LightGraphs.adjacency_matrix(g))
89+
@require LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d" begin
90+
function layout(l::AbstractLayout, g::LightGraphs.AbstractGraph)
91+
layout(l, LightGraphs.adjacency_matrix(g))
92+
end
93+
end
9094
end
9195

9296
"""
@@ -100,6 +104,43 @@ function assertsquare(M::AbstractMatrix)
100104
return a
101105
end
102106

107+
"""
108+
@addcall
109+
110+
Annotate subtypes of `AbstractLayout` to create a lowercase function call for them.
111+
112+
@addcall struct MyLayout{Dim, Ptype} <: AbstractLayout{Dim, Ptype}
113+
para
114+
end
115+
116+
will add the function
117+
118+
mylayout(g; kwargs...) = layout(MyLayout(; kwargs...), g)
119+
"""
120+
macro addcall(expr::Expr)
121+
# assert subtype of abstract layout
122+
@assert expr.head === :struct "Macro not used on struct!"
123+
typedef = expr.args[2]
124+
@assert typedef isa Expr &&
125+
typedef.head === :<: &&
126+
typedef.args[2] isa Expr && # supertype
127+
typedef.args[2].args[1] [:AbstractLayout, :IterativeLayout] "Macro musst be used on subtype of AbstractLayout"
128+
129+
if typedef.args[1] isa Symbol # no type parameters
130+
name = typedef.args[1]
131+
elseif typedef.args[1] isa Expr && typedef.args[1].head === :curly && typedef.args[1].args[1] isa Symbol
132+
name = typedef.args[1].args[1]
133+
else
134+
throw(ArgumentError("Can't find the layout name!"))
135+
end
136+
137+
fname = Symbol(lowercase(String(name)))
138+
return quote
139+
Base.@__doc__ $(esc(expr))
140+
Base.@__doc__ $(esc(fname))(g; kwargs...) = layout($name(; kwargs...), g)
141+
end
142+
end
143+
103144
include("sfdp.jl")
104145
include("buchheim.jl")
105146
include("spring.jl")

src/buchheim.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
export Buchheim
1+
export Buchheim, buchheim
22

33
"""
44
Buchheim(; kwargs...)(adj_matrix)
55
Buchheim(; kwargs...)(adj_list)
6-
layout(algo::Buchheim, adj_matrix)
7-
layout(algo::Buchheim, adj_list)
6+
buchheim(adj_matrix; kwargs...)
7+
buchheim(adj_list; kwargs...)
88
99
Using the algorithm proposed in the paper,
1010
["Improving Walker's Algorithm to Run in Linear Time"](http://dirk.jivas.de/papers/buchheim02improving.pdf)
@@ -20,7 +20,7 @@ and returns coordinates of the nodes.
2020
Determines the size of each of the node. If network size does not match the
2121
length of `nodesize` fill up with `ones` or truncate given parameter.
2222
"""
23-
struct Buchheim{Ptype,T} <: AbstractLayout{2,Ptype}
23+
@addcall struct Buchheim{Ptype,T} <: AbstractLayout{2,Ptype}
2424
nodesize::Vector{T}
2525
end
2626

src/sfdp.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
export SFDP
1+
export SFDP, sfdp
22

33
"""
44
SFDP(; kwargs...)(adj_matrix)
5-
layout(algo::SFDP, adj_matrix)
5+
sfdp(adj_matrix; kwargs...)
66
77
Using the Spring-Electric [model suggested by Yifan Hu](http://yifanhu.net/PUB/graph_draw_small.pdf).
88
Forces are calculated as:
@@ -25,7 +25,7 @@ the nodes.
2525
2626
- `seed=1`: Seed for random initial positions.
2727
"""
28-
struct SFDP{Dim,Ptype,T<:AbstractFloat} <: IterativeLayout{Dim,Ptype}
28+
@addcall struct SFDP{Dim,Ptype,T<:AbstractFloat} <: IterativeLayout{Dim,Ptype}
2929
tol::T
3030
C::T
3131
K::T

src/shell.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
export Shell
1+
export Shell, shell
22

33
"""
44
Shell(; kwargs...)(adj_matrix)
5-
layout(algo::Shell, adj_matrix)
5+
shell(adj_matrix; kwargs...)
66
77
Position nodes in concentric circles. Without further arguments all nodes will
88
be placed on a circle with radius 1.0. Specify placement of nodes using the
@@ -21,7 +21,7 @@ the nodes.
2121
2222
This function started as a copy from [IainNZ](https://github.com/IainNZ)'s [GraphLayout.jl](https://github.com/IainNZ/GraphLayout.jl)
2323
"""
24-
struct Shell{Ptype} <: AbstractLayout{2,Ptype}
24+
@addcall struct Shell{Ptype} <: AbstractLayout{2,Ptype}
2525
nlist::Vector{Vector{Int}}
2626
end
2727

src/spectral.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using LinearAlgebra: diag, eigen, Diagonal
22

3-
export Spectral
3+
export Spectral, spectral
44

55
"""
66
Spectral(; kwargs...)(adj_matrix)
7-
layout(algo::Spectral, adj_matrix)
7+
spectral(adj_matrix; kwargs...)
88
99
This algorithm uses the technique of Spectral Graph Drawing, which is an
1010
under-appreciated method of graph layouts; easier, simpler, and faster
@@ -23,7 +23,7 @@ the nodes.
2323
Vector of weights. If network size does not match the length of `nodesize` use
2424
`ones` instead.
2525
"""
26-
struct Spectral{Ptype,FT<:AbstractFloat} <: AbstractLayout{3,Ptype}
26+
@addcall struct Spectral{Ptype,FT<:AbstractFloat} <: AbstractLayout{3,Ptype}
2727
nodeweights::Vector{FT}
2828
end
2929

src/spring.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
export Spring
1+
export Spring, spring
22

33
"""
44
Spring(; kwargs...)(adj_matrix)
5-
layout(algo::Spring, adj_matrix)
5+
spring(adj_matrix; kwargs...)
66
77
Use the spring/repulsion model of Fruchterman and Reingold (1991) with
88
@@ -28,7 +28,7 @@ the nodes.
2828
2929
- `seed=1`: Seed for random initial positions.
3030
"""
31-
struct Spring{Dim,Ptype} <: IterativeLayout{Dim,Ptype}
31+
@addcall struct Spring{Dim,Ptype} <: IterativeLayout{Dim,Ptype}
3232
C::Float64
3333
iterations::Int
3434
initialtemp::Float64

src/squaregrid.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
export SquareGrid
1+
export SquareGrid, squaregrid
22

33
"""
44
SquareGrid(; kwargs...)(adj_matrix)
5-
layout(algo::SquareGrid, adj_matrix)
5+
squaregrid(adj_matrix; kwargs...)
66
77
Position nodes on a 2 dimensional rectagular grid. The nodes are palced in order
88
from upper left to lower right. To skip positions see `skip` argument.
@@ -18,7 +18,7 @@ the nodes.
1818
`skip=[(i,j)]` means to keep the position in the `i`-th row and `j`-th column
1919
empty.
2020
"""
21-
struct SquareGrid{Ptype,CT} <: AbstractLayout{2,Ptype}
21+
@addcall struct SquareGrid{Ptype,CT} <: AbstractLayout{2,Ptype}
2222
cols::CT
2323
dx::Ptype
2424
dy::Ptype

src/stress.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
using LinearAlgebra: checksquare, norm, pinv, mul!
22
using SparseArrays: SparseMatrixCSC
33

4-
export Stress
4+
export Stress, stress
55

66
"""
77
Stress(; kwargs...)(adj_matrix)
8-
layout(algo::Stress, adj_matrix)
8+
stress(adj_matrix; kwargs...)
99
1010
Compute graph layout using stress majorization. Takes adjacency matrix
1111
representation of a network and returns coordinates of the nodes.
@@ -61,8 +61,8 @@ The main equation to solve is (8) of:
6161
pages={239--250},
6262
}
6363
"""
64-
struct Stress{Dim,Ptype,IT<:Union{Symbol,Int},FT<:AbstractFloat,M<:AbstractMatrix} <:
65-
IterativeLayout{Dim,Ptype}
64+
@addcall struct Stress{Dim,Ptype,IT<:Union{Symbol,Int},FT<:AbstractFloat,M<:AbstractMatrix} <:
65+
IterativeLayout{Dim,Ptype}
6666
iterations::IT
6767
abstols::FT
6868
reltols::FT

test/runtests.jl

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,10 @@ jagmesh_adj = jagmesh()
5858
adj_matrix = adjacency_matrix(g)
5959
positions = @time SFDP(; dim=2, Ptype=Float32, tol=0.1, K=1)(adj_matrix)
6060
@test typeof(positions) == Vector{Point2f0}
61+
@test positions == sfdp(adj_matrix; dim=2, Ptype=Float32, tol=0.1, K=1)
6162
positions = @time SFDP(; dim=3, Ptype=Float32, tol=0.1, K=1)(adj_matrix)
6263
@test typeof(positions) == Vector{Point3f0}
64+
@test positions == sfdp(adj_matrix; dim=3, Ptype=Float32, tol=0.1, K=1)
6365
end
6466
end
6567

@@ -105,8 +107,10 @@ jagmesh_adj = jagmesh()
105107
adj_matrix = adjacency_matrix(g)
106108
positions = @time Stress(; iterations=10, Ptype=Float32)(adj_matrix)
107109
@test typeof(positions) == Vector{Point2f0}
110+
@test positions == stress(adj_matrix; iterations=10, Ptype=Float32)
108111
positions = @time Stress(; iterations=10, dim=3, Ptype=Float32)(adj_matrix)
109112
@test typeof(positions) == Vector{Point3f0}
113+
@test positions == stress(adj_matrix; iterations=10, dim=3, Ptype=Float32)
110114
end
111115
end
112116

@@ -143,8 +147,11 @@ jagmesh_adj = jagmesh()
143147
adj_matrix = adjacency_matrix(g)
144148
positions = @time Spring(; C=2.0, iterations=100, initialtemp=2.0, Ptype=Float32)(adj_matrix)
145149
@test typeof(positions) == Vector{Point2f0}
150+
@test positions == spring(adj_matrix; C=2.0, iterations=100, initialtemp=2.0, Ptype=Float32)
146151
positions = @time Spring(; C=2.0, iterations=100, initialtemp=2.0, Ptype=Float32, dim=3)(adj_matrix)
147152
@test typeof(positions) == Vector{Point3f0}
153+
@test positions ==
154+
spring(adj_matrix; C=2.0, iterations=100, initialtemp=2.0, Ptype=Float32, dim=3)
148155
end
149156
end
150157

@@ -155,8 +162,10 @@ jagmesh_adj = jagmesh()
155162
adj_matrix = adjacency_matrix(g)
156163
positions = @time Spectral()(adj_matrix)
157164
@test typeof(positions) == Vector{Point{3,Float64}}
165+
@test positions == spectral(adj_matrix)
158166
positions = @time Spectral(; Ptype=Float32)(adj_matrix)
159167
@test typeof(positions) == Vector{Point{3,Float32}}
168+
@test positions == spectral(adj_matrix; Ptype=Float32)
160169
end
161170
end
162171

@@ -168,6 +177,7 @@ jagmesh_adj = jagmesh()
168177
adj_matrix = adjacency_matrix(g)
169178
positions = @time Shell()(adj_matrix)
170179
@test typeof(positions) == Vector{Point{2,Float64}}
180+
@test positions == shell(adj_matrix)
171181
end
172182
@testset "Testing Base Case" begin
173183
g = Graph(1)
@@ -206,6 +216,7 @@ jagmesh_adj = jagmesh()
206216
nodesize = [1, 2, 1.5, 3, 0.5, 1, 1]
207217
locs = @time Buchheim(; nodesize)(adj_list)
208218
@test typeof(locs) == Vector{Point{2,Float64}}
219+
@test locs == buchheim(adj_list; nodesize)
209220
locs = @time Buchheim(; Ptype=Float32)(adj_list)
210221
@test typeof(locs) == Vector{Point{2,Float32}}
211222
end
@@ -234,14 +245,17 @@ jagmesh_adj = jagmesh()
234245
M = adjacency_matrix(SimpleGraph(4))
235246
positions = SquareGrid(; Ptype=Int)(M)
236247
@test positions == Point2.([(0, 0), (1, 0), (0, -1), (1, -1)])
248+
@test positions == squaregrid(M; Ptype=Int)
237249

238250
M = adjacency_matrix(SimpleGraph(3))
239251
positions = SquareGrid(; Ptype=Int)(M)
240252
@test positions == Point2.([(0, 0), (1, 0), (0, -1)])
253+
@test positions == squaregrid(M; Ptype=Int)
241254

242255
M = adjacency_matrix(SimpleGraph(5))
243256
positions = SquareGrid(; Ptype=Int, cols=2)(M)
244257
@test positions == Point2.([(0, 0), (1, 0), (0, -1), (1, -1), (0, -2)])
258+
@test positions == squaregrid(M; Ptype=Int, cols=2)
245259
end
246260

247261
@testset "Testing dx,dy" begin
@@ -266,16 +280,16 @@ jagmesh_adj = jagmesh()
266280

267281
@testset "test assert square" begin
268282
using NetworkLayout: assertsquare
269-
M1 = rand(2,4)
283+
M1 = rand(2, 4)
270284
@test_throws ArgumentError assertsquare(M1)
271-
M2 = rand(4,4)
285+
M2 = rand(4, 4)
272286
@test assertsquare(M2) == 4
273-
@test_throws ArgumentError layout(Buchheim(), M1)
274-
@test_throws ArgumentError layout(SFDP(), M1)
275-
@test_throws ArgumentError layout(Shell(), M1)
276-
@test_throws ArgumentError layout(Spectral(), M1)
277-
@test_throws ArgumentError layout(Spring(), M1)
278-
@test_throws ArgumentError layout(SquareGrid(), M1)
279-
@test_throws ArgumentError layout(Stress(), M1)
287+
@test_throws ArgumentError buchheim(M1)
288+
@test_throws ArgumentError sfdp(M1)
289+
@test_throws ArgumentError shell(M1)
290+
@test_throws ArgumentError spectral(M1)
291+
@test_throws ArgumentError spring(M1)
292+
@test_throws ArgumentError squaregrid(M1)
293+
@test_throws ArgumentError stress(M1)
280294
end
281295
end

0 commit comments

Comments
 (0)