Plot a result directly

Built-in plots for results, networks, and models

Plot results the built-in way: time series, rasters, connectivity matrices, and network graphs.

Most objects in TVBO know how to draw themselves. A result can plot its own time series, a network its connectivity, a model its trajectories, so the common views need no plotting code at all. Everything below returns ordinary Matplotlib objects, so any figure can be captured and restyled.

Run a small experiment to plot
import warnings; warnings.filterwarnings("ignore")
import numpy as np
import matplotlib.pyplot as plt
import bsplot
from tvbo import SimulationExperiment, Network, Dynamics, Coupling

bsplot.style.use("tvbo")

res = SimulationExperiment.from_string("""
label: "Visualization demo"
dynamics:
  iri: tvbo:Generic2dOscillator
network:
  number_of_nodes: 8
integration:
  method: Heun
  step_size: 0.1
  duration: 800.0
  transient_time: 100.0
""").run()

sim = res.integration
print("result:", dict(zip(sim.data.dims, sim.data.shape)))
* Owlready2 * Warning: ignoring cyclic subclass of/subproperty of, involving:
  http://uri.interlex.org/tgbugs/uris/readable/atlas/Space

INFO [tvbo.run] [+0s] STEP 1: Running simulation...
INFO [tvbo.run] [+1s]   Simulation period: 800.0 ms, dt: 0.1 ms
INFO [tvbo.run] [+1s]   Transient period: 100.0 ms (settled on (-100.0, 0], warm-started via update_history)
INFO [tvbo.run] [+1s]   Simulation complete.
INFO [tvbo.run] [+1s] Experiment complete.
result: {'time': 8000, 'variable': 2, 'node': 8}

Plotting a result

sim.plot() draws the simulation. A type argument switches the view:

type Shows
timeseries (default) state variables against time
raster all nodes as an image, good for many regions
power_spectrum frequency content
eeg stacked, offset traces in the EEG idiom
Code
fig, axes = plt.subplots(2, 2, figsize=(11, 6))
for ax, kind in zip(axes.ravel(), ["timeseries", "raster", "power_spectrum", "eeg"]):
    sim.plot(type=kind, ax=ax)
    ax.set_title(kind, loc="left", fontsize=10)
plt.tight_layout()
plt.show()
Figure 1: The same simulation in four built-in views.

Passing ax= places a plot into an existing axis, which is how the panel above is built. Without it, each call makes its own figure.

Plotting a network

A connectome has several standard views. plot_matrix() is the quickest look; the per-property helpers take an explicit axis so they can be composed.

Code
net = Network.from_db(atlas="DesikanKilliany", rec="dTOR")

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
net.plot_weights(axes[0], log=True)     # note: these take `ax` positionally
net.plot_lengths(axes[1])
axes[0].set_title("weights (log)", fontsize=10)
axes[1].set_title("tract lengths", fontsize=10)
plt.tight_layout()
plt.show()
Figure 2: Structural connectivity of the Desikan-Killiany connectome: streamline counts (log-scaled) and tract lengths.

plot_overview() assembles a multi-panel summary with one row per edge property, each pairing a brain surface with its matrix, and plot_brain_surface() renders the surface alone. Both are heavier calls that build their own figure:

net.plot_overview(log_weights=True)   # brain surface + matrices, one row per property
net.plot_brain_surface()              # surface only

Plotting a model

A Dynamics can draw its own trajectories. Passing state-variable names selects the projection: one name gives a time course, two give a phase portrait:

Code
d = Dynamics.from_db("Generic2dOscillator")

fig, axes = plt.subplots(1, 2, figsize=(10, 3.6))
d.plot("V", ax=axes[0])
d.plot("V", "W", ax=axes[1])
axes[0].set_title("V(t)", loc="left", fontsize=10)
axes[1].set_title("phase plane", loc="left", fontsize=10)
plt.tight_layout()
plt.show()
Figure 3: The Generic2dOscillator drawn by the model itself: V against time, and the V-W phase plane.

Two structural views are also available, useful when a model is unfamiliar:

d.plot_dependency_tree()   # how variables and parameters depend on each other
d.plot_ontology()          # the model's place in the ontology

A coupling likewise plots its own transfer function. See coupling functions for what that curve means:

Coupling.from_db("Sigmoidal").plot()

Restyling

Every helper returns Matplotlib objects, so nothing is a dead end: capture the axis and treat it like any other plot.

Code
fig, ax = plt.subplots(figsize=(9, 2.8))
sim.plot(type="timeseries", ax=ax)
ax.set_title("Generic2dOscillator, 8 nodes", loc="left")
ax.set_xlabel("time (ms)")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()
Figure 4: A built-in plot, restyled after the fact.

bsplot.style.use("tvbo") (applied at the top of this page) sets the house style for every subsequent figure.

The network as a graph

plot_graph() draws the connectome as a node-link diagram rather than a matrix, which makes hub structure legible. Thresholding keeps the strongest edges:

Code
fig, ax = plt.subplots(figsize=(7, 6))
net.plot_graph(
    ax=ax,
    threshold_percentile=98,     # strongest 2% of edges
    node_labels=False,
    edge_labels=False,
)
plt.tight_layout()
plt.show()
Figure 5: The Desikan-Killiany connectome as a graph, showing only the strongest 2% of edges. Node size and colour follow in-strength.

Vector fields and phase planes

For a two-variable model, vector_field draws the flow the trajectory moves through:

Code
fig, axes = plt.subplots(1, 2, figsize=(11, 4))
sim.plot(type="vector_field", ax=axes[0])
axes[0].set_title("isolated node", loc="left", fontsize=10)
sim.plot(type="vector_field", inputs={"c_glob": 0.3}, ax=axes[1])
axes[1].set_title("with coupling drive $c_{glob}=0.3$", loc="left", fontsize=10)
plt.tight_layout()
plt.show()
Figure 6: Vector field of the Generic2dOscillator with the simulated trajectory overlaid. Coupling inputs are evaluated at zero — the isolated-node flow.

A phase plane needs values for anything the model’s equations expect but the plane does not supply, namely the coupling inputs. By default these are evaluated at \(0\), giving the isolated-node field; pass inputs={"c_glob": ...} to draw the flow at a fixed coupling drive, as on the right above.

plot_graph has two rendering backends. The default networkx one is shown above; format="bsplot" draws the same graph through bsplot’s edge renderer, which gives curved edges and the house styling:

Code
# The bsplot backend builds its own Figure outside pyplot's registry, so return it
# rather than calling plt.show() — which would have nothing to display.
net.plot_graph(
    format="bsplot",
    threshold_percentile=98,
    node_labels=False,
    edge_labels=False,
)
Figure 7: The same thresholded connectome through the bsplot backend.
Requires bsplot ≥ 0.0.8

Older bsplot releases raise NameError: name 'colormaps' is not defined from bsplot/graph/edges.py, which lacked the matplotlib colormap-registry import. If you hit that, upgrade bsplot; the default networkx backend is unaffected either way.