Network Construction

Building a Network

The main type of NetworkDynamics.jl is a Network. A network bundles various component models (edge and vertex models) together with a graph to form a callable object which represents the right hand side (RHS) of the overall dynamical system, see Mathematical Model.

A Network is build by passing a graph g, vertex models vertexm and edge models edgem to the Network constructor:.

nw = Network(g, vertexm, edgem; kwargs...)

Two important keywords for the Network constructor are:

  • execution: Defines the ExecutionStyle of the coreloop, e.g. SequentialExecution{true}(). A execution style is a special Julia object, which tells the backend how to parallelize (e.g. ThreadedExecution{true}() will use native Julia threads to parallelize the RHS call). A list of available executions styles can be found under Execution Types in the API.

  • aggregator: Instructs the backend how to perform the aggregation and which aggregation function to use. Aggregation is the process of creating a single vertex input by reducing over the outputs of adjecent edges of said vertex. The aggregator contains both the function and the algorithm. E.g. SequentialAggregator(+) is a sequential aggregation by summation. A list of availabe Aggregators can be found under Aggregators in the API.

Graphless Constructor

If each of the network components has a "graphelement" metadata, we may omit the explicit graph.

nw = Network(vertexm, edgem)

The graphelement metadata can be set using the following syntax:

VertexModel(; ..., vidx=1)         # places vertex at position 1
EdgeModel(; ..., src=1, dst=2)     # places edge between 1 and 2
EdgeModel(; ..., src=:v1, dst=:v2) # places edge between vertices with names `:v1` and `:v2`

Building VertexModels

This chapter will walk you through the most important aspects of defining a custom vertex model. For a list of all keyword arguments please check out the docstring of VertexModel.

As an example, we'll construct an second order kuramoto model.

function kuramoto_f!(dv, v, esum, p, t)
    M, P, D = p
    dv[1] = v[2]
    dv[2] = (P - D*v[2] + esum[1])/M
    nothing
end
function kuramoto_g!(y, v, esum, p, t)
    y[1] = v[1]
    nothing
end
VertexModel(; f=kuramoto_f!, g=kuramoto_g!, dim=2, pdim=3, outdim=1)
VertexModel :VertexM FeedForward()
 ├─ 2 states: [v₁, v₂]
 ├─ 1 output: [o]
 └─ 3 params: [p₁, p₂, p₃]

Those keywords are the minimum metadata we need to provide.

However there is a problem: the vertex is classified as a FeedForward vertex, which is unnecessary. We can improve the implementation of g according to the Feed Forward Behavior section.

function kuramoto_g_noff!(y, v, p, t)
    y[1] = v[1]
    nothing
end
VertexModel(; f=kuramoto_f!, g=kuramoto_g_noff!, dim=2, pdim=3, outdim=1)
VertexModel :VertexM NoFeedForward()
 ├─ 2 states: [v₁, v₂]
 ├─ 1 output: [o]
 └─ 3 params: [p₁, p₂, p₃]

To simplify your programming and avoid explicitly writing the above trivial output function you can use StateMask. By writing

VertexModel(; f=kuramoto_f!, g=StateMask(1:1), dim=2, pdim=3)
VertexModel :VertexM PureStateMap()
 ├─ 2 states: [v₁, v₂]
 ├─ 1 output: [v₁]
 └─ 3 params: [p₁, p₂, p₃]

we are instructing the vertex model, that the output is part of the states x[1:1]. This results in the following changes:

  • outdim is removed because it can be inferred from StateMask
  • outsym is not a generic :o any more but inferred from the state symbols.

We can be even less verbose by writing g=1:1 or just g=1.

Lastly, we define improved names for our states and parameters as well as assigning a position in the graph to enable the graphless network construction. Whenever you provide a sym keyword the corresponding dim keyword stops being neccessary. So, we end up with a relatively short definition

VertexModel(; f=kuramoto_f!, g=1,
              sym=[:θ, :ω], psym=[:M=>1, :P=>0.1, :D=>0],
              insym=[:P_nw], name=:swing, vidx=1)
VertexModel :swing PureStateMap() @ Vertex 1
 ├─ 1 input:  [P_nw]
 ├─ 2 states: [θ, ω]
 ├─ 1 output: [θ]
 └─ 3 params: [M=1, P=0.1, D=0]

Building EdgeModels

This chapter walks you through the most important aspects when defining custom edge models. For a list of all keyword arguments please check the docstring of EdgeModel.

As an example edge model we define a standard sinusoidal coupling between the vertices in our network. The full definition is:

function edge_f!(de, e, vsrc, vdst, p, t)
    nothing
end
function edge_g!(ysrc, ydst, e, vsrc, vdst, p, t)
    ydst[1] = p[1] * sin(vsrc[1] - vdst[1])
    ysrc[1] = -ydst[1]
end
EdgeModel(; f=edge_f!, g=edge_g!, dim=0, pdim=1, outdim=1)
EdgeModel :StaticEdgeM FeedForward()
 ├─   0 states:  []  
 ├─ 1/1 outputs: src=[src₊o] dst=[dst₊o]
 └─   1 param:   [p] 

This is a purely "static" edge without internal states. This means we can omit f and dim entirely. Also, we can define a variant of g without the e input

function edge_g_ff!(ysrc, ydst, vsrc, vdst, p, t)
    ydst[1] = p[1] * sin(vsrc[1] - vdst[1])
    ysrc[1] = -ydst[1]
end
EdgeModel(;g=edge_g_ff!, pdim=1, outdim=1)
EdgeModel :StaticEdgeM PureFeedForward()
 ├─   0 states:  []  
 ├─ 1/1 outputs: src=[src₊o] dst=[dst₊o]
 └─   1 param:   [p] 

which classifies as a PureFeedForward edge. In cases like this, where the edge is actually anti-symmetrical we can define a single sided output function and wrap it in an AntiSymmetric object:

function edge_g_s!(ydst, vsrc, vdst, p, t)
    ydst[1] = p[1] * sin(vsrc[1] - vdst[1])
end
EdgeModel(;g=AntiSymmetric(edge_g_ff!), pdim=1, outdim=1)
EdgeModel :StaticEdgeM FeedForward()
 ├─   0 states:  []  
 ├─ 1/1 outputs: src=[₋o] dst=[o]
 └─   1 param:   [p] 

This can also lead to briefer output naming. Available single sided wrappers are:

Once again we can add additonal data like defining a src and dst index

function edge_g_s!(ydst, vsrc, vdst, p, t)
    ydst[1] = p[1] * sin(vsrc[1] - vdst[1])
end
EdgeModel(;g=AntiSymmetric(edge_g_ff!), psym=:K=>1, outsym=:P, insym=:θ, src=1, dst=4)
EdgeModel :StaticEdgeM FeedForward() @ Edge 1=>4
 ├─ 1/1 inputs:  src=[src₊θ] dst=[dst₊θ]
 ├─   0 states:  []  
 ├─ 1/1 outputs: src=[₋P] dst=[P]
 └─   1 param:   [K=1]