Layout Interface
At its core, each layout algorithm is a mapping
graph ↦ node_positionswhere each layout has several parameters. The main goal of the following interface is to keep the separation between parameters and function call. Each layout is implemented as subtype of AbstractLayout.
NetworkLayout.AbstractLayout — TypeAbstractLayout{Dim,Ptype}Abstract supertype for all layouts. Each layout Layout <: AbstractLayout needs to implement
layout(::Layout, adj_matrix)::Vector{Point{Dim,Ptype}}which takes the adjacency matrix representation of a network and returns a list of node positions. Each Layout object holds all of the necessary parameters.
The type parameters specify the returntype Vector{Point{Dim,Ptype}}:
Dim: the dimensionality of the layout (i.e. 2 or 3)Ptype: the type of the returned points (i.e.Float32orFloat64)
By implementing layout the Layout also inherits the function-like property
Layout(; kwargs...)(adj_matrix) -> node_positionsTherefore, 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.
There are some additional guidelines:
- All of the parameters should be keyword arguments, i.e. it should be allways possible to call
LayoutAlgorithm()without specifying any parameters. - Algorithms should allways return
Vector{Point{Dim,Ptype}}. If the type or dimensions can be altered use the keywordsdimandPtypefor it. - Some parameters may depend on the specific network (i.e. length of start positions vector). If possible, there should be a fallback option (i.e. truncate the list of start positions if network is to small or append with random values).
It is convenient to define the lowercase functions
layoutalgorithm(g; kwargs...) = layout(LayoutAlgorihtm(; kwargs...), g)which can done using this macro:
NetworkLayout.@addcall — Macro@addcallAnnotate subtypes of AbstractLayout to create a lowercase function call for them.
@addcall struct MyLayout{Dim, Ptype} <: AbstractLayout{Dim, Ptype}
para
endwill add the function
mylayout(g; kwargs...) = layout(MyLayout(; kwargs...), g)Iterative Layouts
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:
NetworkLayout.IterativeLayout — TypeIterativeLayout{Dim,Ptype} <: AbstractLayout{Dim,Ptype}Abstract supertype for iterative layouts. Instead of implementing layout directly, subtypes Algorithm<:IterativeLayout need to implement the iterator interface
Base.iterate(iter::LayoutIterator{<:Algorithm})
Base.iterate(iter::LayoutIterator{<:Algorithm}, state)where the iteration item is a Vector{Point{Dim,Ptype}} and the iteration state depends on the algorithm.
By implementing the iterator interface the Algorithm inherits the layout and function-like call
layout(algo::Algorithm, adj_matrix) -> node_postions
Algorithm(; kwargs...)(adj_matrix) -> node_positionsOne can instantiate an iterable object LayoutIterator
NetworkLayout.LayoutIterator — TypeLayoutIterator{T<:IterativeLayout,M<:AbstractMatrix}(algorithm, adj_matrix)This type bundles an IterativeLayout with an adjacency matrix to form an iterable object whose items are the node positions.
Example
for p in LayoutIterator(Stress(), adj_matrix)
# do stuff with positions p
end