from tvbo import Dynamics, SimulationExperiment
model = Dynamics.from_db("ReducedWongWangExcInh")
exp = SimulationExperiment(dynamics=model)
result = exp.run("jax", duration=10_000)
resultExperiment
└── integration
data: (819200, 2, 1, 1)
Building, configuring, and running a SimulationExperiment
A SimulationExperiment is the central runnable object in TVBO. It bundles everything required to simulate a brain network model into one declarative specification:
dynamics — the local neural mass / population model (a Dynamics)network — the brain network (connectome + parcellation + node dynamics)coupling — the long-range coupling function between regionsintegration — the numerical integrator (method, dt, duration, noise)monitors / observations — derived signals (BOLD, FC, PSD, …)stimulation, algorithms, optimizations, explorations, continuations — additional analysis layersThe same object can be serialized to YAML, rendered as code for several backends, executed, or exported to BIDS / openMINDS — all from a single in-memory specification that mirrors the LinkML schema.
Dynamics objectThe simplest experiment is just a model. Defaults are filled in for coupling, network, integrator, and monitors:
TVBO is declarative about where information comes from. Every component in an experiment is either:
from_file / from_string.iri CURIE identifying the source. The IRI’s prefix selects the source (tvbo: for the built-in ontology, but also e.g. neuroml:, kg:, …); the rest of the dict is backfilled from that source.from tvbo import SimulationExperiment
exp = SimulationExperiment(
dynamics={"name": "ReducedWongWang", "iri": "tvbo:ReducedWongWangExcInh"},
coupling={"name": "Linear", "iri": "tvbo:Linear"},
network={
"parcellation": {"iri": "tvbo:DesikanKilliany"},
"tractogram": {"iri": "tvbo:dTOR"},
},
integration={"method": "Heun", "duration": 10_000, "noise": None},
)The IRI is the load-bearing piece — a bare name string is not semantically resolvable across multiple sources. (For coupling only, a bare string is accepted as a shorthand that auto-prefixes tvbo:, but prefer the explicit iri form so the source is unambiguous.)
See Defining Networks, Coupling Functions and Integrators for the per-section details.
TVBO ships a curated set of full experiment specifications — load any of them by name:
['AdaptiveExponentialIF_Ex8',
'EI_Tuning_FIC_EIB_Optimization',
'Epileptor2D_LEMS',
'FitzHughNagumo1969_Ex9',
'FitzHughNagumo_Ex9',
'Generic2dOscillator_LEMS',
'IntegrateAndFire_Ex0',
'Izhikevich2007_Ex2',
'JR_MEG_FrequencyGradient_Optimization',
'JansenRit-bifurcation']
Experiments are round-trippable through YAML (see Code Generation & Export):
| Method | Source |
|---|---|
SimulationExperiment.from_db(name) |
built-in tvbo/database/studies/*.yaml |
SimulationExperiment.from_file(path) |
local YAML file |
SimulationExperiment.from_string(yaml) |
inline YAML string |
SimulationExperiment.from_pyrates(path) |
PyRates YAML |
SimulationExperiment.from_pydantic(obj) |
Pydantic schema instance |
SimulationExperiment.from_tvb_simulator(sim) |
live tvb.simulator.Simulator |
SimulationExperiment.from_bids(path) |
BIDS BEP034 derivative |
SimulationExperiment.from_platform(...) |
TVB-O platform API |
SimulationExperiment.from_openminds(src) |
openMINDS JSON-LD |
['dynamics', 'network', 'coupling']
{'state': [Eq(Derivative(S_e(t), t), (gamma_e*H_e(t))*(1 - S_e(t)) - S_e(t)/tau_e),
Eq(Derivative(S_i(t), t), gamma_i*H_i(t) - S_i(t)/tau_i)],
'functions': [],
'derived_parameters': [],
'derived': [Eq(J_N_S_e(t), J_N*S_e(t)),
Eq(coupling(t), (G*J_N)*(c_glob + local_coupling*S_e(t))),
Eq(x_e(t), a_e*((-J_i*S_i(t) + (w_p*J_N_S_e(t) + (I_ext + I_o*W_e))) + coupling(t)) - b_e),
Eq(x_i(t), a_i*(lamda*coupling(t) + ((I_o*W_i + J_N_S_e(t)) - S_i(t))) - b_i),
Eq(H_e(t), x_e(t)/(1 - exp((-d_e)*x_e(t)))),
Eq(H_i(t), x_i(t)/(1 - exp((-d_i)*x_i(t))))],
'parameters': {G: 2.0,
I_ext: 0.0,
I_o: 0.382,
J_N: 0.15,
J_i: 1.0,
W_e: 1.0,
W_i: 0.7,
a_e: 310.0,
a_i: 615.0,
b_e: 125.0,
b_i: 177.0,
d_e: 0.16,
d_i: 0.087,
gamma_e: 0.000641,
gamma_i: 0.001,
lamda: 0.0,
tau_e: 100.0,
tau_i: 10.0,
w_p: 1.4},
'coupling': {}}
id: 3
model: ReducedWongWang
dynamics:
name: ReducedWongWang
iri: tvbo:ReducedWongWangExcInh
parameters:
G:
name: G
definition: Global coupling scaling
value: 2.0
domain:
lo: 0.0
hi: 10.0
step: 0.01
log_scale: false
description: Global coupling scaling
I_ext:
name: I_ext
definition: External stimulation paramet
exp.run(format=..., **kwargs) configures delays, generates code for the requested backend, executes it, and returns an ExperimentResult:
result = exp.run("jax", duration=5_000)
print(type(result).__name__, "→", result.integration.data.shape)ExperimentResult → (409600, 2, 87, 1)
format= |
Backend | Notes |
|---|---|---|
"tvboptim" (default) |
tvboptim (JAX-based) | Optimisation-friendly; supports algorithms & explorations. |
"jax" / "autodiff" |
Pure JAX kernel | Fastest forward integration, jit-compiled. |
"tvb" |
The Virtual Brain | Reference NumPy implementation. |
"pyrates" |
PyRates | Symbolic frontend, good for custom models. |
"networkdynamics" |
NetworkDynamics.jl | Julia backend (requires julia). |
"mtk" |
ModelingToolkit.jl | Symbolic Julia backend. |
"bifurcation" |
BifurcationKit.jl / numcont | Continuation analysis. |
exp.run() accepts these common kwargs:
duration — overrides integration.durationinitial_conditions — TimeSeries or array; defaults from collect_initial_conditions()jit=False for JAX)execute() vs run()exp.execute(format=...) returns the prepared simulator object (a TVB Simulator, a JIT-compiled JAX kernel, a tvboptim namespace, …). Use it when you want to drive the simulation manually.exp.run(format=...) calls execute() and runs it, then wraps the output in an ExperimentResult.Most users never need to call configure() directly — run() does it. It performs auto-fixes such as disabling delay logic when the connectome has no path lengths or conduction speed is infinite.
You can render the simulator code without executing it (useful for debugging or for using the kernel outside TVBO):
import jax
from tvbo.data.types import TimeSeries
from tvbo.utils import Bunch
import jax.numpy as jnp
def cfun(weights, history, current_state, p, delay_indices, t):
n_node = weights.shape[0]
b, a = p.b, p.a
x_j = jnp.array([
history[0, delay_indices[0].T, delay_indices[1]],
])
S_e = x_j[0]
pre = x_j
pre = pre.reshape(-1, n_node ,n_node)
def op(x): return jnp.sum(weights * x, axis=-1)
gx = jax.vmap(op, in_axes=0)(pre)
return b + a*gx
import jax.numpy as jnp
import jax.scipy as jsp
def dfun(current_state, t, cX, _p):
# Par
See Code Generation & Export for the full list of supported targets.
| Goal | Call |
|---|---|
| Round-trippable YAML | exp.to_yaml("path.yaml") |
| BIDS BEP034 derivative | exp.to_bids("output/derivatives/tvbo") |
| openMINDS JSON-LD | exp.to_openminds("experiment.jsonld") |
| LEMS / NeuroML | exp.to_lems(...) |
| Standalone code file | exp.save_code("out/", file_name="kernel.py") |
| Markdown / HTML report | exp.report(format="markdown") |
For exporting simulation results (time series, observations, metadata) in BIDS layout, see Working with Results → BIDS Export.