Rover
This repository is still under development.
Statically partitioned hybrid simulator that couples:
- BNGsim (ODE / analytical Jacobian) on a deterministic SBML partition
- StochMod (constrained tau-leap) on a stochastic SBML partition
over a shared nanomolar array. Orchestration is a plain Python loop (SingleCell-shaped): both modules advance from the same pre-step state, then exchange results with write ownership on overlap species.
Where one step happens
HybridSimulator.run
└─ rover.engine.run_steps
├─ s0 = counts # shared nM
├─ stoch.advance_from(s0, dt) # StochModModule (nM ↔ molecules; TFs as nM)
├─ bng.advance_from(s0, dt) # BngsimModule (identity nM; mRNA frozen)
└─ exchange(s0, s_stoch, s_bng) # owner-takes-all on overlap
Edit coupling in [rover/engine.py](rover/engine.py) (run_steps / exchange_counts)
or advance_from under [rover/modules/](rover/modules/).
Partitioning is file membership plus stoichiometric write ownership:
overlap mRNA/genes are stoch-owned; overlap TFs (modifiers only in StochMod) are
det-owned. Exchange takes the owner’s post-step value (not Δ_stoch + Δ_bng).
Det-owned TFs are passed into StochMod as nM (SingleCell-like), not molecule counts.
Quickstart
StochMod and BNGsim install from PyPI with Rover:
pip install -e ".[dev]"
python basic_hybrid.py
pytest
StochMod compiles per-model C propensity code on first load of each SBML, so a
C compiler (gcc, clang, or MSVC cl) must be available. Wheels from PyPI
already include the Python extension itself.
For local StochMod development, install an editable clone first
(pip install -e /path/to/StochMod) so it shadows the PyPI package, then
install Rover as above.
from rover import HybridSimulator
sim = HybridSimulator(
"tests/data/LR/deterministic-interactions.xml",
"tests/data/LR/stochastic-gene-expression.xml",
dt=1.0,
)
sim.update("cyt_mrna__LIGAND_", 0.001582)
sim.update({"kTL1_1": 2.0, "kTC1_1": 0.01})
traj = sim.run(t_end=60.0) # shape (61, n_species); live state stays 1-D nM
df = sim.to_dataframe() # columns: time + species ids (nM)
# Large runs — pre-size a memmap .npy (OS page cache; kernels never open the file):
# traj = sim.run(t_end=259200, dt=30, results_path="out/traj.npy")
sim.reset()
One-shot helper (rebuilds models each call — prefer HybridSimulator for sweeps):
from rover import run_hybrid
traj, index = run_hybrid(det_xml, stoch_xml, t_end=50.0, dt=1.0)
Shared store currency is nanomolar. BNGsim storage is already nM (identity
bridge). StochMod converts to molecule counts at the module boundary using SBML
compartment volumes (nM · V · N_A · 1e-9), matching SingleCell.
Trajectory storage
| Piece | Where | Why |
|---|---|---|
Live _counts |
1-D float64 nM in RAM |
Hot path for coupling |
| Trajectory | (n_points, n_species) memory or memmap .npy |
History only; one row write per step |
Do not have BNGsim/StochMod dump to disk each step — that would dominate cost. The orchestrator copies the live vector into the next trajectory row (sequential write; memmap is fine because the OS caches pages).
Performance notes
Absolute time is orchestrator-owned. Trajectory times are t0 + step*dt.
Each coupling step advances a local interval [0, dt] in BNGsim (interactive
clock rewound before advance(dt)). StochMod leaps once by dt per call.
Hybrid coupling is not the same cost model as a standalone batch run:
| Mode | What happens |
|---|---|
StochMod.run(0, T, dt) / bngsim.Simulator.run(...) |
One Python call; entire trajectory stays in C |
sim.run(t_end=T) with coupling dt |
T/dt Python↔C round-trips (set_state / advance / convert) |
So t_end=259200, dt=1 means 259 200 coupling steps. Prefer a larger
coupling dt when the biology allows it (e.g. dt=30 → ~8640 steps for 3 days).
BNGsim defaults: codegen on, jacobian="analytical", rtol=1e-4, atol=1e-6,
max_steps=1e8. If codegen fails, Rover falls back to the interpreted RHS; if
analytical Jacobian construction fails, it falls back to finite differences.
On some Windows consoles codegen can fail with a charmap encoding error.
Preparing your own SBML pair
Rover does not ingest a single model. You split your network into two SBML files and pass them as:
HybridSimulator(deterministic_sbml, stochastic_sbml, dt=...)
The bundled examples happen to be gene expression plus protein binding (mRNA, genes, TFs). That biology is not required. Any species can live on either side of the split. Rover never classifies a species by what it represents; only by which file lists it and whether it is a reactant/product there.
What each file contains
| File | Simulator | Put here |
|---|---|---|
| Deterministic SBML | BNGsim (ODE) | Reactions you want integrated continuously |
| Stochastic SBML | StochMod (tau-leap) | Reactions you want as discrete molecule events |
A species may appear in one file or both. IDs are the join key: the same
species id in both files is one shared state entry. Exclusive species
(present in only one file) are fine.
Typical pattern, independent of molecule type:
- Put a reaction in exactly one file — the side that should fire it.
- If the other side’s rate laws need that species as an input, also declare it in the other file, usually as a modifier (not a reactant/product).
- Keep compartment
ids and sizes consistent for shared species.
Do not put the same reaction in both files. Species that are reactants/products in both files are ambiguous (see ownership below); avoid that unless you intend the name-based fallback.
Write ownership (who updates shared species)
After each coupling step, overlap species take the owner’s post-step
value. Ownership is inferred from stoichiometry, not from names like
mrna / gene / prot:
- Reactant or product only in the stochastic file → StochMod owns it. BNGsim holds it fixed over the ODE window (so the deterministic file can still read it, e.g. as a modifier).
- Reactant or product only in the deterministic file → BNGsim owns it. StochMod sees it as nanomolar (not molecule counts) and does not write it back — the usual pattern for a regulator that appears only as a modifier in stochastic rate laws.
- Modifier-only in a file does not confer ownership.
- If a species is a reactant/product in both files, Rover falls back to
a name heuristic (
mrna/gene→ stochastic, otherwise deterministic). Do not rely on that for a custom model: keep stoichiometric participation on one side only.
So: a low-copy ion channel, a promoter, or an enzyme can be stochastic; a high-abundance metabolite or a transcription factor can be deterministic. Put the species as reactant/product in the file that should own it, and as a modifier (if needed) in the other.
Units and ICs
Shared state is nanomolar. Use SBML initialConcentration in nM,
compartment sizes in litres, and substance units of nanomole (the example
files define substance as mole with scale="-9"). StochMod converts
stoch-owned species to molecule counts with nM · V · N_A · 1e-9;
det-owned overlap is passed through as nM, so stochastic kinetic laws that
read those species should be written in nM, not counts.
If both files set an overlap species’ initial condition, BNGsim’s value is used and a mismatch is logged.
Example layout (bundled LR pair):
[tests/data/LR/deterministic-interactions.xml](tests/data/LR/deterministic-interactions.xml)
and
[tests/data/LR/stochastic-gene-expression.xml](tests/data/LR/stochastic-gene-expression.xml).
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rover_sim-0.1.0.tar.gz.
File metadata
- Download URL: rover_sim-0.1.0.tar.gz
- Upload date:
- Size: 26.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3accb6677d94c4ace6865d503409e53989783afa685cf29511bc807fb6f215a0
|
|
| MD5 |
d118fdacd25a2d64c52dbd91d806c80b
|
|
| BLAKE2b-256 |
8ebf4e53e433c0c16bf2b52a9a11911aa64ac6cea484d0cf6a34c273d9d48916
|
Provenance
The following attestation bundles were made for rover_sim-0.1.0.tar.gz:
Publisher:
publish.yml on JonahRileyHuggins/Rover
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rover_sim-0.1.0.tar.gz -
Subject digest:
3accb6677d94c4ace6865d503409e53989783afa685cf29511bc807fb6f215a0 - Sigstore transparency entry: 2537172611
- Sigstore integration time:
-
Permalink:
JonahRileyHuggins/Rover@ce508c41b2df2234c63d03d219952dcf9fe5a70b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/JonahRileyHuggins
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ce508c41b2df2234c63d03d219952dcf9fe5a70b -
Trigger Event:
release
-
Statement type:
File details
Details for the file rover_sim-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rover_sim-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
89f57cd9abb7a430a7eb4eb2c4097786d5ecfde85cdd80059a8aabefc3aabb65
|
|
| MD5 |
c389fef118e99eed801ad399f1d3ca1f
|
|
| BLAKE2b-256 |
8c815f63dc4d90bf35cc1ac876abe8c1d31c485423cc5c4bf72d9c6622b9a2fa
|
Provenance
The following attestation bundles were made for rover_sim-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on JonahRileyHuggins/Rover
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rover_sim-0.1.0-py3-none-any.whl -
Subject digest:
89f57cd9abb7a430a7eb4eb2c4097786d5ecfde85cdd80059a8aabefc3aabb65 - Sigstore transparency entry: 2537173145
- Sigstore integration time:
-
Permalink:
JonahRileyHuggins/Rover@ce508c41b2df2234c63d03d219952dcf9fe5a70b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/JonahRileyHuggins
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@ce508c41b2df2234c63d03d219952dcf9fe5a70b -
Trigger Event:
release
-
Statement type: