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)
Part of the running example, where stage 1 adds the model, the network and the clock.
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 or population model, a Dynamicsnetwork: the brain network, its connectome, parcellation and node dynamicscoupling: the long-range coupling function between regionsintegration: the numerical integrator, its method, step size, duration and noisemonitors and observations: derived signals such as BOLD, FC and PSDstimulation, algorithms, optimizations, explorations and continuations: additional analysis layersThe same object can be serialized to YAML, rendered as code for several backends, executed, or exported to BIDS or 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 and 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"},
network={
"parcellation": {"iri": "tvbo:DesikanKilliany"},
"tractogram": {"iri": "tvbo:dTOR"},
"coupling": {"Linear": {"iri": "tvbo:Linear"}},
},
integration={"method": "Heun", "duration": 10_000, "noise": None},
)A coupling function acts along the edges of a network, so it is declared on the network, as a keyed collection, since a network may carry several. The key is the coupling’s name; writing it a second time inside the entry is redundant.
The IRI is the load-bearing piece: a bare name string is not semantically resolvable across multiple sources.
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',
'Delay_Speed_Synchronization',
'EI_Tuning_FIC_EIB_Optimization',
'Epileptor2D_LEMS',
'FitzHughNagumo1969_Ex9',
'FitzHughNagumo_Ex9',
'Generic2dOscillator_LEMS',
'Hopf_Pareto_ParallelOpt',
'IntegrateAndFire_Ex0',
'Izhikevich2007_Ex2']
Experiments are round-trippable through YAML (see Code Generation & Export):
The file is read by the shared loader, so it supports keyed collections, scalar shortcuts, anchors, merge keys and !include. See Writing TVB-O YAML.
A key repeated twice in the same mapping is an error, not a last-one-wins override.
| 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_platform(...) |
TVB-O platform API |
SimulationExperiment.from_openminds(src) |
openMINDS JSON-LD |
['dynamics', 'network', 'coupling']
{'state': [Eq(Derivative(S_e(t), t), gamma_e*(1 - S_e(t))*H_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*(I_ext + I_o*W_e - J_i*S_i(t) + w_p*J_N_S_e(t) + coupling(t)) - b_e),
Eq(x_i(t), a_i*(I_o*W_i + lamda*coupling(t) + 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},
'units': {G: None,
I_ext: None,
I_o: 'nA',
J_N: None,
J_i: None,
W_e: None,
W_i: None,
a_e: 'per_nC',
a_i: 'per_nC',
b_e: 'Hz',
b_i: 'Hz',
d_e: 's',
d_i: 's',
gamma_e: None,
gamma_i: None,
lamda: None,
tau_e: 'ms',
tau_i: 'ms',
w_p: None,
S_e(t): None,
S_e: None,
S_i(t): None,
S_i: None,
J_N_S_e(t): None,
J_N_S_e: None,
coupling(t): None,
coupling: None,
x_e(t): None,
x_e: None,
x_i(t): None,
x_i: None,
H_e(t): None,
H_e: None,
H_i(t): None,
H_i: None,
c_glob: None,
local_coupling: None},
'coupling': {}}
id: 3
model: ReducedWongWang
part: main
dynamics:
name: ReducedWongWang
iri: tvbo:ReducedWongWangExcInh
parameters:
G:
name: G
definition: Global coupling scaling
value: 2.0
domain:
enforce: none
lo: 0.0
hi: 10.0
step: 0.01
log_scale: false
description: Global coupling scaling
I_ext:
name: I_ext
definit
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)
The format= (default tvboptim) selects the engine: jax for the fastest forward integration, tvb for a reference result, plus pyrates, networkdynamics, brian2, and the bifurcationkit / auto analysis backends. The full catalogue — capabilities, CLI invocation, and how sweeps are placed — is on Run on a backend.
exp.run() accepts these common kwargs:
duration: overrides integration.durationinitial_conditions: a 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, because 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 logging
import jax
from tvbo.data.types import TimeSeries
import jax.numpy as jnp
logger = logging.getLogger("tvbo.run")
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]],
]
)
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
def dfun(current_state, t, cX, _p):
See Code Generation & Export for the full list of supported targets.
| Goal | Call |
|---|---|
| Round-trippable YAML | exp.to_yaml("path.yaml") |
| openMINDS JSON-LD | exp.to_openminds("experiment.jsonld") |
| LEMS / NeuroML | NeuroMLAdapter(exp).render_code() |
| 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.
exp.run() returns