Layout Interface

At its core, each layout algorithm is a mapping

graph ↦ node_positions

where 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.


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. Float32 or Float64)

By implementing layout the Layout also inherits the function-like property

Layout(; kwargs...)(adj_matrix) -> node_positions

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.

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 keywords dim and Ptype for 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:

[email protected]Macro

Annotate subtypes of AbstractLayout to create a lowercase function call for them.

@addcall struct MyLayout{Dim, Ptype} <: AbstractLayout{Dim, Ptype}

will 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:

IterativeLayout{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}, 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_positions

One can instantiate an iterable object LayoutIterator

LayoutIterator{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.


for p in LayoutIterator(Stress(), adj_matrix)
    # do stuff with positions p