Skip to content

Edit tutorial contents #253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
f9d5262
Update index.md
BethProedrou Apr 29, 2025
3f47a4f
Update index.md
BethProedrou Apr 29, 2025
ad9bd53
Further updates to the Update index.md
BethProedrou Apr 30, 2025
0aaf778
Improved the "Where to begin" section
BethProedrou Apr 30, 2025
ffff152
improved Installation section
BethProedrou May 1, 2025
12a4821
added some missing commands to install the package
BethProedrou May 1, 2025
9f92084
added link to DAE
BethProedrou May 1, 2025
02f0868
improved the link
BethProedrou May 1, 2025
31f7f96
Improving Mathematical Model
BethProedrou May 1, 2025
27075a5
Impoved the mathematical model text
May 3, 2025
4810a1e
Merge branch 'main' into edit_tutorial_contents
hexaeder May 12, 2025
334d867
remove "diff style" in index.md
hexaeder May 12, 2025
49f8074
replaced the old style of changes with the new one
BethProedrou May 13, 2025
76cf0b4
half-way save
BethProedrou May 13, 2025
8a34eaf
fixed phrasing
BethProedrou May 14, 2025
65dee53
Fixed phrasing
BethProedrou May 14, 2025
728f0b6
improved phrasing
BethProedrou May 14, 2025
f5ba8de
fixed change I made
BethProedrou May 14, 2025
c780541
placed basic complex network systems information in a blockquote
May 26, 2025
5955040
improved readability in IDE
May 26, 2025
c08ceae
improved readability in IDE
May 26, 2025
1f04988
Fixed and added notes
May 26, 2025
ecfac0c
rearranged code to improve readability (work in progress)
May 28, 2025
2e555c6
working
May 28, 2025
bb3993f
working
May 29, 2025
f9e33db
fixed issues causing it not to run
May 29, 2025
c7749de
Merge branch 'main' into edit_tutorial_contents
May 29, 2025
47dfd31
Getting Started Tutorial v.1
May 31, 2025
84f1f2b
address comments
hexaeder Jun 2, 2025
45b002e
address more comments
hexaeder Jun 2, 2025
5da970c
deleted comment
Jun 23, 2025
f79e169
deleted unneeded comment
Jun 23, 2025
1987454
deleted a comment
Jun 23, 2025
3c66526
Merge branch 'edit_tutorial_contents' of https://github.com/JuliaDyna…
Jun 23, 2025
67584ca
Merge branch 'main' into edit_tutorial_contents
Jun 23, 2025
d8855e9
removed comment about a missing image
Jul 1, 2025
b4426e9
incorporated the changes requested
Jul 1, 2025
3bc5b81
Refactored: added simple network image to the file
Jul 9, 2025
2f02a6e
itermediate state
Jul 14, 2025
a719662
intermediate state
Jul 14, 2025
726fcf4
made changes to improve the readability of the page
Jul 17, 2025
fbd941e
made changes to improve the readability of the page
Jul 17, 2025
95d809e
changed plots to make them more understandable
Jul 18, 2025
5dda53f
improved readability
Jul 18, 2025
961ad26
improved readability
Jul 18, 2025
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
42 changes: 29 additions & 13 deletions docs/src/index.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,56 @@
# NetworkDynamics

A package for working with dynamical systems on complex networks. NetworkDynamics.jl provides an interface between [Graphs.jl](https://github.com/JuliaGraphs/Graphs.jl) and [DifferentialEquations.jl](https://github.com/SciML/DifferentialEquations.jl).
It allows for high performant simulation of dynamic networks by describing the local dynamics on the edges and vertices of the graph.
*NetworkDynamics.jl* is a package ~~for working with~~ *to simulate* dynamical systems ~~on~~ *within* complex networks. ~~NetworkDynamics.jl~~ *It* provides an interface between *the* [Graphs.jl](https://github.com/JuliaGraphs/Graphs.jl) and *the* [DifferentialEquations.jl](https://github.com/SciML/DifferentialEquations.jl) *packages* ~~.~~ *and* ~~it allows for high performant simulation of~~ *faciliates the simulation of highly efficient* dynamic networks by describing the local dynamics on the edges and vertices of the graph.

The behavior of a node or an edge can be described by algebraic equations, by differential algebraic equation (DAEs) in mass matrix form or by ordinary differential equations (ODE).
*Complex network systems are composed by the entities that comprise them (the nodes) and the relationships that connect each entity with one another (the edges). The graphical depictions of such networks are called graphs. The simplest network (which can be seen in Figure 1) is composed of two entities (so two nodes) who are only connected to each other. This connection between the two is the edge of the system. Complex networks are composed of multiple nodes and edges, with most nodes connected to multiple other nodes with multiple edges*** *(@Hans: can you created the graph of such a network and place in here?)*

The central construction is the function [`Network`](@ref) that receives functions describing the local dynamics on the edges and nodes of
a graph `g` as inputs, and returns a composite function compatible with the
DifferentialEquations.jl calling syntax.
The behavior of a node or an edge can be described ~~by~~ *through the use of* a) algebraic equations, b) ~~by~~ differential algebraic equation (DAEs) in mass matrix form ~~or~~ c) ~~by~~ ordinary differential equations (ODE).

The ~~central construction~~ *core of the package* is the function [`Network`](@ref)*.* ~~that~~ *It accepts the* ~~receives~~ functions describing the local dynamics on the edges and nodes of ~~a~~ *the* graph `g` as inputs, and returns a composite function compatible with the DifferentialEquations.jl calling syntax.

```julia
nd = Network(g, vertex_dynamics, edge_dynamics)
nd(dx, x, p, t)
```

Main features:
- Clear separation of local dynamics and topology: you can easily change topology of your system or switching out dynamical components.
- High performance when working with heterogeneous models (which means heaving different local dynamics in different parts of your network).
- [Symbolic Indexing](@ref) into solutions and states: NetworkDynamics keeps track of the states of the individual subsystems.
- Different execution schemes: NetworkDynamics exploits the known inter-dependencies between components to auto parallelize execution, even on GPUs!
- Equation based models: use [ModelingToolkit.jl](https://docs.sciml.ai/ModelingToolkit/dev/) to define local dynamics, use `NetworkDynamics.jl` to combine them into large networks!
- Clear separation of local dynamics and topology: you can easily change *the* topology of your system or switch~~ing~~ out dynamic~~al~~ components.
- High performance when working with heterogeneous models *:* ~~(which means heaving~~ *you can have* different local dynamics in different parts of your network ~~)~~.
- [Symbolic Indexing](@ref) into solutions and states: NetworkDynamics keeps track of the states of ~~the~~ *each* individual subsystem~~s~~.
- ~~Different~~ *Diverse* execution schemes: NetworkDynamics exploits the known inter-dependencies between components to auto parallelize execution, even on GPUs!
- Equation based models: ~~use [ModelingToolkit.jl](https://docs.sciml.ai/ModelingToolkit/dev/) to define local dynamics, use `NetworkDynamics.jl` to combine them into large networks!~~ *you can model local dynamics using [ModelingToolkit.jl](https://docs.sciml.ai/ModelingToolkit/dev/) and them combine them into larger networks by using `NetworkDynamics.jl`!*


## Where to begin?
Check out the [Mathematical Model](@ref) to understand the underlying modelling ideas of NetworkDynamics followed by the page on [Network Construction](@ref) to learn how to implement you own models.
~~Check out the [Mathematical Model](@ref) to understand the underlying modelling ideas of NetworkDynamics followed by the page on [Network Construction](@ref) to learn how to implement you own models.~~

*To learn how to implement your own models and understand the underlying modelling ideas of NetworkDynamics you should first read the [Mathematical Model](@ref) section, followed by section [Network Construction](@ref).*

If you prefer to look at some concrete code first check out the [Getting Started](@ref) tutorial!


## Installation

Installation is straightforward with Julia's package manager.
Install Julia:
- https://julialang.org/install/
- Find your OS and follow the instructions for the installation

Install NetworkDynamics.jl with Julia's package manager:
```julia-repl
(v1.11) pkg> add NetworkDynamics
```

Next you need to install the Julia package Revise:
```julia-repl
import Pkg; Pkg.add("Revise")
```

Last you need to install the Julia package LiveServer:
```julia-repl
import Pkg; Pkg.add("LiveServer")
```



## Reproducibility

Expand Down
102 changes: 71 additions & 31 deletions docs/src/mathematical_model.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
# Mathematical Model
The basic mathematical model of `NetworkDynamics.jl` splits up the system into two parts: vertex and edge components.
The basic mathematical model of `NetworkDynamics.jl` splits ~~up~~ the system ~~it~~ *in* two parts: *the* vertex and
*the* edge components.

The main goal of `NetworkDynamics.jl` is ~~,~~ to express the overall network dynamics as a
[Differential-Algebraic-Equation (DAE)](https://mathworld.wolfram.com/Differential-AlgebraicEquation.html)

The main goal of `NetworkDynamics.jl` is, to express the overall network dynamics as a Differential-Algebraic-Equation (DAE)
```math
M\,\frac{\mathrm{d}}{\mathrm{d}t}u = f^{\mathrm{nw}}(u, p, t)
```
where M is a (possibly singular) mass matrix, $u$ is the internal state vector of the system, $p$ are the parameters and $t$ is the time.
To make this compatible with the solvers for `OrdinaryDiffEq.jl`, the created [`Network`](@ref) object is a callable object
where M is a (possibly singular) mass matrix, $u$ is the internal state vector of the system, $p$ are the parameters
and $t$ is the time.
To make this compatible with the solvers ~~for~~ ***used in*** `OrdinaryDiffEq.jl`, the ~~created~~ ***generated***
[`Network`](@ref) object is a callable object
```
nw(du, u, p, t) # mutates du
```
with stored mass matrix information to build an `ODEProblem` based on the `Network`.
***where the matrix information necessary to build the simulated `Network` and by extension the (`ODEProblem`) is stored.***
~~with stored mass matrix information to build an `ODEProblem` based on the `Network`.~~
(@Hans: what is the definition of the ODEProblem? what is it? it is not clear to me)

Instead of defining $f^{\mathrm{nw}}$ by hand, `ND.jl` helps you to build it automatically based on a list of decentralized nodal and edge dynamics, so-called `VertexModel` and `EdgeModel` objects.
Each component model $\mathrm c$ is modeled as general input-output-system
Instead of defining $f^{\mathrm{nw}}$ by hand, `ND.jl` helps you ~~to~~ build it automatically based on a list of
decentralized nodal and edge dynamics, ***the*** so-called `VertexModel` and `EdgeModel` objects.
Each component model $\mathrm c$ is modeled as ***a*** general input-output-system:

```math
\begin{aligned}
Expand All @@ -22,16 +30,28 @@ y^{\mathrm c} &= g^{\mathrm c}(x^\mathrm{c}, i_{\mathrm c}, p_{\mathrm c}, t)
\end{aligned}
```

where $M_{\mathrm{c}}$ is the component mass matrix, $x^{\mathrm c}$ are the component states, $i^{\mathrm c}$ are the *inputs* of the component and $y^{\mathrm c}$ is the *output* of the component. It is possible to have $\mathrm{dim}(x^{\mathrm{c}}) = 0$ and thus no internal states.
where $M_{\mathrm{c}}$ is the component mass matrix, $x^{\mathrm c}$ are the component states, $i^{\mathrm c}$ are the
***inputs*** of the component and $y^{\mathrm c}$ is the ***output*** of the component.
~~It is possible to have $\mathrm{dim}(x^{\mathrm{c}}) = 0$ and thus no internal states.~~
***If $\mathrm{dim}(x^{\mathrm{c}}) = 0$, the number of internal states is 0.***

In the ~~network~~ context ***of the network***, the **output of the edges are flow variables** ~~.~~ ***and*** ~~The~~
***the*** **outputs of vertices are potential variables**. In interconnection (@Hans: what do you mean here?), the
*flow* on the edges depends on the *potentials* at both ends as inputs. The *potentials* of the nodes depend on the
incoming *flows* from all connected edges as an input. (Here, flow and potentials are meant in a conceptional and not
necessarily physical way.)

In the network context, the **output of the edges are flow variables**. The **outputs of vertices are potential variables**. In interconnection, the *flow* on the edges depends on the *potentials* at both ends as inputs. The *potentials* of the nodes depend on the incoming *flows* from all connected edges as an input. (Here, flow and potentials are meant in a conceptional and not necessarily physical way.)
~~*
*~~
```@raw html
<img src="../assets/mathmodel.svg" width="100%"/>
```
@Hans: the image seems to be missing here

## Vertex Models
Specifically, a (single-layer) vertex model has one input, and one output.
The input is an aggregation/reduction over all *incident edge outputs*,
(@Hans: I think a depiction of a vertex model is needed here)
~~Specifically, a~~ ***A*** (single-layer) vertex model has one input, and one output.
The input is an aggregation/reduction over all ***of the*** *incident edge outputs*,
```math
i^{\mathrm v} = \mathop{\mathrm{agg}}\limits_k^{\text{incident}} y^{\mathrm e}_k \qquad\text{often}\qquad
i^{\mathrm v} = \sum_k^{\text{incident}} y^{\mathrm e}_k
Expand All @@ -57,13 +77,23 @@ vertf = VertexModel(; f=fᵥ, g=gᵥ, mass_matrix=Mᵥ, ...)
```

## Edge Models
In contrast to vertex models, edge models in general have *two* inputs and *two* outputs, both for source and destination end of the edge.
(@Hans: I think a depiction of an edge model is needed here)
In contrast to vertex models, edge models in general have *two* inputs and *two* outputs, ***for*** both ~~for~~
***the*** source and *the* destination end of the edge.
We commonly use `src` and `dst` to describe the source and destination end of an edge respectively.

!!! note "On the directionality of edges"
Mathematically, in a system defined on an undirected graph there is no difference between the edge $(1,2)$ and $(2,1)$, the edge has no direction. However, from an implementation point of view we always need to have some kind of ordering for function arguments, state order and so on. For undirected graphs, `Graphs.jl` chooses the direction of an edge `v1->v2` such that `v1 < v2`.
`NOTE:`
Mathematically, in a system defined on an undirected graph there is no difference between ~~the~~ edge $(1,2)$ and
*edge* $(2,1)$, ***because*** the edge has no direction.
However, from an implementation point of view we always need to have some kind of ordering for function arguments,
state order and so on. (@Hans I am not sure what "for function arguments, state order and so on" means)
For undirected graphs, `Graphs.jl` chooses the direction of an edge `v1->v2` such that `v1 < v2`.

The *inputs* of the edge are just the outputs of the two nodes at both ends. The output is split into two: the `dst` output goes to the input of the vertex at the destination end, the `src` output goes to the input of the vertex at the `src` end.
The *inputs* of the edge are ~~just~~ the outputs of the two nodes at both ***their*** ends. The output is split into
two ***parts***:
the `dst` output goes to the input of the vertex at the destination end, the `src` output goes to the input of the
vertex at the `src` end.
(@Hans: I think a depiction of an edge model is needed here)

The full model of an edge
```math
Expand All @@ -73,7 +103,7 @@ y^{\mathrm e}_{\mathrm{dst}} &= g_\mathrm{dst}^{\mathrm e}(u^{\mathrm e}, y^{\ma
y^{\mathrm e}_{\mathrm{src}} &= g_\mathrm{src}^{\mathrm e}(u^{\mathrm e}, y^{\mathrm v}_{\mathrm{src}}, y^{\mathrm v}_{\mathrm{dst}}, p^{\mathrm e}, t)
\end{aligned}
```
corresponds to the Julia functions
***which ***corresponds to the Julia functions ***:***
```julia
function fₑ(dxₑ, xₑ, v_src, v_dst, pₑ, t)
# mutate dxᵥ
Expand All @@ -86,9 +116,10 @@ end
vertf = EdgeModel(; f=fₑ, g=gₑ, mass_matrix=Mₑ, ...)
```

The sign convention for both outputs of an edge must be identical,
typically, a positive flow represents a flow *into* the connected vertex.
This is important, because the vertex only receives the flows, it does not know whether the flow was produce by the source or destination end of an edge.
The sign convention for both outputs of an edge must be identical, ***so*** typically, a positive flow represents a
flow *into* the connected vertex.
This is important, because the vertex only receives the flows, it does not know whether the flow was produce by the
source or ***the ***destination end of an edge.
```
y_src y_dst
V_src o───←─────────→───o V_dst
Expand All @@ -97,23 +128,29 @@ This is important, because the vertex only receives the flows, it does not know


### Single Sided Edge Outputs
Often, edge outputs will possess some symmetry which makes it more convenient to define "single sided" edge output functions
Often, edge outputs will possess some symmetry ***.*** ~~which~~ ***This*** makes it more convenient to define
"single sided" edge output functions ***:***
```julia
function g_single(y, xᵥ, v_src, v_dst, pₑ, t)
# mutate y
nothing
end
```
There are multiple wrappers available to automaticially convert them into double-sided edge output functions:
There are multiple wrappers available to ~~automaticially~~ ***automatically*** convert them into double-sided edge
output functions:

- `Directed(g_single)` builds a double-sided function *which only couples* to the destination side.
- `Symmetric(g_single)` builds a double-sided function in which both ends receive `y`.
- `AntiSymmetric(g_single)` builds a double-sided function where the destination receives `y` and the source receives `-y`.
- `Fiducial(g_single_src, g_singl_dst)` builds a double-sided edge output function based on two single sided functions.

(@Hans: A figure depicting the options presented above is needed here)

## Feed Forward Behavior
The most general version of the component models can contain direct feed forwards from the input, i.e. the edge output might depend directly on the connected vertices or the vertex output might depend directly on the aggregated edge input.
The most ~~general~~ ***generic*** version of the component models can contain direct feed forwards from the input,
i.e. the edge output might depend directly on the connected vertices or the vertex output might depend directly on the
aggregated edge input.
(@Hans: this explanation of what a "feed forward behaviour" is, is too short and not descriptive enough.
A more detailed analysis is needed here)

Whenever possible, you should define output functions without feed forwards, i.e.
```julia
Expand All @@ -127,15 +164,19 @@ gₑ([y_src], y_dst, xᵥ, v_src, v_dst, pₑ, t)
```

NetworkDynamics cannot couple two components with feed forward to each other.
It is always possible to transform feed forward behavior to an internal state `x` with mass matrix entry zero to circumvent this problem. This transformation can be performed automatically by using [`ff_to_constraint`](@ref).
***But,*** It is always possible to transform feed forward behavior to an internal state `x` with mass matrix entry zero to
circumvent this problem. This transformation can be performed automatically by using [`ff_to_constraint`](@ref).


!!! warning "Feed Forward Vertices"
As of 11/2024, vertices with feed forward are not supported at all. Use [`ff_to_constraint`](@ref) to transform them into vertex model without FF.
`WARNING: "Feed Forward Vertices"`
As of 11/2024, vertices with feed forward are not supported at all. Use [`ff_to_constraint`](@ref) to transform them
into vertex model without FF.

Concretely, NetworkDynamics distinguishes between 4 types of feed forward behaviors of `g` functions based on the [`FeedForwardType`](@ref) trait.
The different types the signature of provided function `g`.
Based on the signatures avaialable, ND.jl will try to find the correct type automaticially. Using the `ff`
keyword in the constructors, the user can enforce a specific type.
Concretely, NetworkDynamics distinguishes between 4 types of feed forward behaviors of `g` functions based on the
[`FeedForwardType`](@ref) trait.
The different types the signature of provided function `g`. (@Hans: I am not sure what you are trying to say here. Please rephrase)
Based on the signatures ~~avaialable~~ ***available***, ND.jl will try to find the correct type ~~automaticially~~
***automatically***. Using the `ff` keyword in the constructors, the user can enforce a specific type.

**[`PureFeedForward()`](@ref)**
```julia
Expand Down Expand Up @@ -165,4 +206,3 @@ g!(out_dst, x) # single-sided edge
g!(out_src, out_dst, x) # double-sided edge
g!(v_out, x) # single layer vertex
```

Loading