B-Simulant LibraryGetting Started

Getting Started with B-Simulant

This guide walks you through installing and using the B-Simulant library for local simulations.

Installation

Basic Installation

pip install bsim

With Adapters

# SBML support (tellurium/libroadrunner)
pip install bsim[tellurium]
 
# ONNX/ML model support
pip install bsim[ml]
 
# All adapter extras (tellurium + ml)
pip install bsim[adapters]
 
# SimUI extras
pip install bsim[ui]

From Source

git clone https://github.com/biosimulant/B-Simulant.git
cd B-Simulant
pip install -e ".[dev]"

Your First Simulation

1. Import the Library

import bsim
from bsim import BioWorld, WiringBuilder, FixedStepSolver

2. Create a World

The BioWorld is the central orchestrator:

world = BioWorld(solver=FixedStepSolver())

3. Add Modules

Use WiringBuilder for declarative composition:

from bsim.packs.neuro import StepCurrent, IzhikevichPopulation, SpikeMonitor
 
wb = WiringBuilder(world)
input_current = StepCurrent(I=10.0, schedule=[(0.1, 0.8, 15.0)])
neurons = IzhikevichPopulation(n=100, preset="RS")
monitor = SpikeMonitor()
 
wb.add("input", input_current)
wb.add("neurons", neurons)
wb.add("monitor", monitor)

4. Connect Modules

Wire outputs to inputs:

wb.connect("input.out.current", ["neurons.in.current"])
wb.connect("neurons.out.spikes", ["monitor.in.spikes"])
wb.apply()

5. Run the Simulation

result = world.simulate(steps=1000, dt=0.001)
 
print(f"Simulated {result['steps']} steps (t={result['time']:.2f})")

6. Visualize Results

# Get visualization data from monitors
visuals = monitor.visualize()
 
# Returns:
# {
#   "render": "image",
#   "data": { "src": "data:image/svg+xml;base64,...", "width": 600, "height": 300 }
# }

Complete Example: Predator-Prey

import bsim
from bsim.packs.ecology import (
    Environment,
    OrganismPopulation,
    PredatorPreyInteraction,
    PopulationMonitor
)
 
# Create world with fixed time stepping
world = bsim.BioWorld(solver=bsim.FixedStepSolver())
 
# Build the composition
wb = bsim.WiringBuilder(world)
 
# Environment
wb.add("env", Environment(temperature=20.0))
 
# Add populations with species presets
wb.add("rabbits", OrganismPopulation(name="Rabbits", initial_count=100, preset="rabbit"))
wb.add("foxes", OrganismPopulation(name="Foxes", initial_count=10, preset="fox"))
 
# Add interaction dynamics
wb.add("predation", PredatorPreyInteraction(
    predation_rate=0.01,
    conversion_efficiency=0.1
))
 
# Add monitoring
monitor = PopulationMonitor()
wb.add("monitor", monitor)
 
# Wire the ecosystem
wb.connect("env.out.conditions", ["rabbits.in.conditions", "foxes.in.conditions"])
wb.connect("rabbits.out.population_state", ["predation.in.prey_state", "monitor.in.population_state"])
wb.connect("foxes.out.population_state", ["predation.in.predator_state", "monitor.in.population_state"])
wb.connect("predation.out.predation", ["rabbits.in.predation"])
wb.connect("predation.out.food_gained", ["foxes.in.food_gained"])
 
# Apply wiring
wb.apply()
 
# Simulate 100 time units with dt=0.01
result = world.simulate(steps=10000, dt=0.01)
 
# Get population time series
vis = monitor.visualize()
print(vis["data"]["title"])

Using Adapters

SBML Model

from bsim.adapters import TelluriumAdapter
 
# Load SBML model
adapter = TelluriumAdapter(model_path="glycolysis.xml", expose=["S1", "S2"])
adapter.setup({})
 
# Or from string
adapter = TelluriumAdapter(sbml_string=sbml_string, expose=["S1", "S2"])
adapter.setup({})
 
# Advance and read outputs
adapter.advance_to(10.0)
outputs = adapter.get_outputs()
print(outputs["S1"].value)

ONNX Model

from bsim.adapters import MLAdapter, BioSignal
 
# Load ONNX model (input/output names default to the model's I/O names)
adapter = MLAdapter(model_path="model.onnx")
adapter.setup({})
 
adapter.set_inputs({
    "input_1": BioSignal(source="client", name="input_1", value=[1.0, 2.0, 3.0], time=0.0),
    "input_2": BioSignal(source="client", name="input_2", value=[4.0, 5.0, 6.0], time=0.0),
})
adapter.advance_to(0.0)
 
outputs = adapter.get_outputs()
print(outputs)

Hybrid Composition

from bsim.adapters import TelluriumAdapter, MLAdapter, TimeBroker
 
# Adapters run under the TimeBroker runtime (separate from BioWorld module wiring).
sbml = TelluriumAdapter(
    model_path="pathway.xml",
    expose=["ATP", "glucose"],
)
ml = MLAdapter(
    model_path="response.onnx",
    inputs={"ATP": "x1", "glucose": "x2"},
    outputs={"y": "efficacy"},
)
 
broker = TimeBroker()
broker.register("metabolism", sbml, time_scale="seconds")
broker.register("predictor", ml, time_scale="seconds")
broker.connect("metabolism.ATP", "predictor.ATP")
broker.connect("metabolism.glucose", "predictor.glucose")
broker.setup()
 
for _t in broker.run(duration=100.0, dt=1.0):
    pass
 
efficacy = ml.get_outputs()["efficacy"].value
print("efficacy:", efficacy)

Configuration Files

YAML Configuration

# composition.yaml
modules:
  prey:
    class: bsim.packs.ecology.OrganismPopulation
    args:
      name: Prey
      initial_count: 100
      preset: rabbit
  predator:
    class: bsim.packs.ecology.OrganismPopulation
    args:
      name: Predator
      initial_count: 10
      preset: fox
  interaction:
    class: bsim.packs.ecology.PredatorPreyInteraction
    args:
      predation_rate: 0.01
 
wiring:
  - from: prey.out.population_state
    to: [interaction.in.prey_state]
  - from: predator.out.population_state
    to: [interaction.in.predator_state]
  - from: interaction.out.predation
    to: [prey.in.predation]
  - from: interaction.out.food_gained
    to: [predator.in.food_gained]

Load and run:

import bsim
 
world = bsim.BioWorld(solver=bsim.FixedStepSolver())
world.load_wiring("composition.yaml")
result = world.simulate(steps=1000, dt=0.1)

Note: YAML loading requires pyyaml (included in bsim[dev]).

SimUI: Web Interface

Launch a local web interface for your simulation:

from bsim.simui import Interface, Number, Button, EventLog, VisualsPanel
 
ui = Interface(
    world,
    controls=[
        Number("steps", 1000, minimum=100, maximum=10000, step=100),
        Number("dt", 0.01, minimum=0.001, maximum=0.1, step=0.001),
        Button("Run")
    ],
    outputs=[
        EventLog(),
        VisualsPanel()
    ]
)
 
# Launch in browser
ui.launch(port=8080)

Access at http://localhost:8080/ui/.

Next Steps