from tvbo import Dynamics, SimulationExperiment, Network
dyn = Dynamics.from_db("Generic2dOscillator")
print({sv: dyn.state_variables[sv].initial_value for sv in dyn.state_variables}){'V': 0.1, 'W': 0.1}
The initial state of a simulation is the value of every state variable on every node at \(t = 0\). TVBO resolves it from four layered sources, each overriding the previous:
state_variables.<name>.initial_value in the dynamics YAML.state: blocks on individual nodes.distribution on a state variable, activated for trial-based explorations.integration.transient > 0, a pre-simulation is run and its final state replaces the IC.The shape of state.initial_state.dynamics in the generated tvboptim code is (n_states, n_nodes).
Each state variable in the model YAML may carry initial_value. It is broadcast across all nodes.
Attach a state: block to any node to override that node’s IC. Other nodes keep the model default.
net = Network.from_string("""
label: Visual Pathway
nodes:
- id: 0
label: V1
state:
V: {value: 0.5}
W: {value: -0.2}
- id: 1
label: V2
state:
V: {value: 0.0}
- {id: 2, label: MT}
edges: []
""")Programmatic equivalent:
from tvbo.datamodel.tvbopydantic import StateVariable
exp.network.nodes[0].state = {
'V': StateVariable(name='V', value=0.5),
'W': StateVariable(name='W', value=-0.2),
}The tvboptim template emits one at[<sv_idx>].set(jnp.array([...])) per overridden state variable.
Add a distribution to a state variable; tvboptim then samples ICs per trial when an exploration with n_trials > 1 is active.
state_variables:
V:
initial_value: 0.1
distribution:
name: Uniform # Uniform | Gaussian (a.k.a. Normal)
seed: 42
domain: { lo: -2.0, hi: 2.0 }For Gaussian, mean = \((lo+hi)/2\) and std = \((hi-lo)/4\) (clamped to the domain).
from tvbo.datamodel.tvbopydantic import Distribution, Domain, Exploration
exp.dynamics.state_variables['V'].distribution = Distribution(
name='Uniform', domain=Domain(lo=-2.0, hi=2.0), seed=42
)
exp.dynamics.state_variables['W'].distribution = Distribution(
name='Uniform', domain=Domain(lo=-6.0, hi=6.0), seed=42
)
# Activate sampling: n_trials samples are drawn via jax.vmap
exp.explorations['ic_trials'] = Exploration(name='ic_trials', n_trials=10)
res = exp.run()Each trial draws an independent IC of shape (n_states, n_nodes) and runs the simulation in parallel via jax.vmap. Use average: trials on the exploration to reduce across trials.
A plain exp.run() without an exploration ignores the distribution. Sampling only happens when at least one Exploration requests n_trials > 1. To add a random_initial_conditions flag for single runs, open an issue.
Set integration.transient to discard a settling period. The transient simulation honours the IC rules above, and its final state replaces the IC of the main run.
In the generated code:
| Source | Scope | Triggered by |
|---|---|---|
state_variables.initial_value |
global | always (default) |
network.nodes[i].state |
per-node | state: block on a node |
state_variables.distribution |
per-trial | Exploration(n_trials=N) with \(N > 1\) |
integration.transient |
global | integration.transient > 0 |
Search the output for initial_state.dynamics to see exactly how the IC is assembled for your experiment.