# =============================================================================
# Network Diffusion - Getting Started with NetworkDynamics.jl
# =============================================================================
# Source: https://juliadynamics.github.io/NetworkDynamics.jl/stable/generated/getting_started_with_network_dynamics/
#
# Diffusion on an undirected network. Each node state evolves as the sum of
# incoming edge flows.
# Vertex has no intrinsic dynamics; edge is antisymmetric: e = v_src - v_dst.
# =============================================================================
id: 100
label: "Network Diffusion"
description: >
Diffusion on an undirected network. Each node state evolves as the sum
of incoming edge flows. The edge computes the antisymmetric difference
between source and destination states: e = v_src - v_dst.
Recreates the NetworkDynamics.jl Getting Started tutorial.
references:
- "https://juliadynamics.github.io/NetworkDynamics.jl/stable/generated/getting_started_with_network_dynamics/"
# -- Local Dynamics ------------------------------------------------------------
dynamics:
name: Diffusion
label: "Diffusion vertex"
description: >
Node with no internal dynamics. The rate of change of the single
state variable v equals the aggregated incoming edge flow (esum).
Corresponds to the discrete Laplacian: dx_i = sum_j A_ji (x_j - x_i).
system_type: continuous
autonomous: true
parameters: {}
coupling_inputs:
c_flow:
description: "Aggregated edge flow into this vertex (sum of incident edge outputs)"
state_variables:
v:
label: "Diffusion state"
description: "Abstract quantity (e.g. temperature, concentration) diffusing on the network"
equation:
rhs: "c_flow"
initial_value: 0.0
distribution:
name: Gaussian
seed: 1
domain: { lo: -3.0, hi: 3.0 }
variable_of_interest: true
# -- Network -------------------------------------------------------------------
network:
label: "20-node Barabási-Albert network"
description: >
Undirected Barabási-Albert scale-free random graph with 20 nodes
and average degree 4. Matches the original tutorial.
number_of_nodes: 20
graph_generator:
name: barabasi_albert
type: BarabasiAlbert
parameters:
k: { name: k, value: 4 }
coupling:
DiffusiveCoupling:
name: DiffusiveCoupling
label: "Diffusive Coupling"
description: >
Antisymmetric edge function: output is the difference between
source and destination vertex states.
g_dst(v_src, v_dst) = v_src - v_dst;
g_src(v_src, v_dst) = v_dst - v_src (by AntiSymmetric wrapper).
delayed: false
pre_expression:
rhs: "x_j - x_i"
description: "Discrete gradient: source state minus destination state"
# -- Integration ---------------------------------------------------------------
integration:
description: "Explicit Runge-Kutta (Tsit5) with dt=0.01 over t in [0, 2]"
method: Tsit5
step_size: 0.01
duration: 2.0
time_scale: "a.u."
Network Diffusion
Getting started with NetworkDynamics.jl via tvbo
Replicates the NetworkDynamics.jl Getting Started tutorial — diffusion on an undirected network with 20 nodes. A single scalar state per node, antisymmetric diffusive coupling, and convergence to equilibrium.
YAML Specification
Model Report
* Owlready2 * Warning: ignoring cyclic subclass of/subproperty of, involving:
http://uri.interlex.org/tgbugs/uris/readable/atlas/Space
Network Diffusion
Diffusion on an undirected network. Each node state evolves as the sum of incoming edge flows. The edge computes the antisymmetric difference between source and destination states: e = v_src - v_dst. Recreates the NetworkDynamics.jl Getting Started tutorial.
Local Dynamics: Diffusion vertex
Node with no internal dynamics. The rate of change of the single state variable v equals the aggregated incoming edge flow (esum). Corresponds to the discrete Laplacian: dx_i = sum_j A_ji (x_j - x_i).
system: continuous; autonomous: True; modes: 1; state variables: 1; parameters: 0.
State Equations
\[\dot{v} = c_{flow}\]
State Variables
| Variable | Initial Value | Equation | Domain / Sampling | Flags | Description |
|---|---|---|---|---|---|
| \(v\) | 0 | differential (order 1) | Gaussian [-3, 3] axis=space seed=1 | recorded | Abstract quantity (e.g. temperature, concentration) diffusing on the network |
Dimensional Check
| Equation | Dimensional check | Detail |
|---|---|---|
| \(\dot{v}\) | underdetermined | v has no declared unit |
Coupling Inputs
| Input | Source | Dimension | Keys | Description |
|---|---|---|---|---|
| c_flow | — | 1 | — | Aggregated edge flow into this vertex (sum of incident edge outputs) |
Brain Network: 20-node Barabási-Albert network
Undirected Barabási-Albert scale-free random graph with 20 nodes and average degree 4. Matches the original tutorial.
| Setting | Value |
|---|---|
| Regions | 20 |
| Conduction velocity | 3.0 mm_per_ms |
| Distance unit | mm |
| Nodes | 20 explicit nodes |
| Graph generator | BarabasiAlbert |
| Weights | shape=20x20, dtype=float64 |
Coupling: Diffusive Coupling
Antisymmetric edge function: output is the difference between source and destination vertex states. g_dst(v_src, v_dst) = v_src - v_dst; g_src(v_src, v_dst) = v_dst - v_src (by AntiSymmetric wrapper).
| Property | Value |
|---|---|
| Symmetry | directed |
Pre-synaptic: \(c_{\text{pre}} = x_{j} - x_{i}\)
Numerical Integration
Explicit Runge-Kutta (Tsit5) with dt=0.01 over t in [0, 2]
| Setting | Value |
|---|---|
| Method | Tsit5 |
| Time step | \(\Delta t = 0.01\) ms |
| Duration | 2.0 ms |
| Absolute tolerance | 1e-10 |
| Relative tolerance | 1e-10 |
| Stages | 1 |
| Delayed state history | True |
References
- https://juliadynamics.github.io/NetworkDynamics.jl/stable/generated/getting_started_with_network_dynamics/
Generated Julia Code
from tvbo import SimulationExperiment
exp = SimulationExperiment.from_file("yaml/diffusion.yaml")
Code(exp.render_code("networkdynamics"), language='julia')using Graphs
using NetworkDynamics
using OrdinaryDiffEqTsit5
function Diffusion_f!(dx, x, esum, p, t)
v = x[1]
c_flow = esum[1]
dx[1] = c_flow
nothing
end
vertex_Diffusion = VertexModel(;
f = Diffusion_f!,
g = StateMask(1:1),
sym = [:v],
name = :Diffusion,
)
function DiffusiveCoupling_edge_g!(e_dst, v_src, v_dst, p, t)
e_dst[1] = v_src[1] .- v_dst[1]
nothing
end
edge_DiffusiveCoupling = EdgeModel(;
g = AntiSymmetric(DiffusiveCoupling_edge_g!),
outsym = [:coupling],
name = :DiffusiveCoupling,
)
g = barabasi_albert(20, 4)
nw = Network(g, vertex_Diffusion, edge_DiffusiveCoupling)
s = NWState(nw)
using Random
rng = MersenneTwister(1)
for node in 1:nv(g)
s.v[node, :v] = (-3.0 + 3.0) / 2 .+ (3.0 - -3.0) / 6 .* randn(rng)
end
tspan = (0.0, 2.0)
prob = ODEProblem(nw, uflat(s), tspan, pflat(s))
sol = solve(prob, Tsit5(); saveat=0.01)
adj_matrix = Float64.(adjacency_matrix(g))
using Random: MersenneTwister
function spring_layout(g; seed=42, iterations=50, k=1.0)
rng = MersenneTwister(seed)
n = nv(g)
pos = randn(rng, n, 2)
for _ in 1:iterations
disp = zeros(n, 2)
for i in 1:n, j in (i+1):n
d = pos[i, :] - pos[j, :]
dist = max(norm(d), 0.01)
rep = k^2 / dist
disp[i, :] .+= d / dist * rep
disp[j, :] .-= d / dist * rep
end
for e in edges(g)
i, j = src(e), dst(e)
d = pos[j, :] - pos[i, :]
dist = max(norm(d), 0.01)
att = dist^2 / k
disp[i, :] .+= d / dist * att
disp[j, :] .-= d / dist * att
end
for i in 1:n
dl = max(norm(disp[i, :]), 0.01)
pos[i, :] .+= disp[i, :] / dl * min(dl, 0.1)
end
end
return pos
end
using LinearAlgebra: norm
node_positions = spring_layout(g)
using Plots
plot(sol; ylabel="state", xlabel="time", title="Diffusion on 20-node network")
Run & Plot
ts = exp.run(format="networkdynamics")
ts.plot()Detected IPython. Loading juliacall extension. See https://juliapy.github.io/PythonCall.jl/stable/compat/#IPython

Results
Shape: (201, 1, 20)
Time range: 0.0 – 2.0
Final state std: 0.002159
(Should converge to ~0 as all nodes reach equilibrium)
Animate
from pathlib import Path
ani = ts.animate("v", format="dots")
Path("_output").mkdir(exist_ok=True)
ani.save("_output/diffusion.gif", writer="pillow", fps=20)