Metadata-Version: 2.4
Name: biosimulant
Version: 0.0.16
Summary: Open-source local simulation runtime and CLI for Biosimulant
Project-URL: Documentation, https://github.com/Biosimulant/biosim#readme
Project-URL: Issues, https://github.com/Biosimulant/biosim/issues
Project-URL: Source, https://github.com/Biosimulant/biosim
Author-email: Demi <bjaiye1@gmail.com>
License-Expression: MIT
License-File: LICENSE.txt
Classifier: Development Status :: 4 - Beta
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.10
Requires-Dist: argcomplete>=3.5
Requires-Dist: fastapi<1,>=0.110
Requires-Dist: numpy>=1.26
Requires-Dist: pyyaml>=6
Requires-Dist: uvicorn>=0.23
Provides-Extra: all
Requires-Dist: httpx>=0.26; extra == 'all'
Requires-Dist: libcellml<0.7,>=0.6.3; extra == 'all'
Requires-Dist: onnxruntime>=1.18; extra == 'all'
Requires-Dist: pytest-cov>=4; extra == 'all'
Requires-Dist: pytest>=7; extra == 'all'
Requires-Dist: scipy<2,>=1.11; extra == 'all'
Requires-Dist: tomli>=2; (python_version < '3.11') and extra == 'all'
Provides-Extra: cellml
Requires-Dist: libcellml<0.7,>=0.6.3; extra == 'cellml'
Requires-Dist: scipy<2,>=1.11; extra == 'cellml'
Provides-Extra: dev
Requires-Dist: httpx>=0.26; extra == 'dev'
Requires-Dist: pytest-cov>=4; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: tomli>=2; (python_version < '3.11') and extra == 'dev'
Provides-Extra: ml
Requires-Dist: onnxruntime>=1.18; extra == 'ml'
Description-Content-Type: text/markdown

# biosimulant

[![PyPI - Version](https://img.shields.io/pypi/v/biosimulant.svg)](https://pypi.org/project/biosimulant)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/biosimulant.svg)](https://pypi.org/project/biosimulant)

Composable simulation runtime + UI layer for orchestrating runnable biomodules.

`biosimulant` is the primary package, import namespace, and CLI name. The
existing `biosim` Python import path and `python -m biosim` command remain
supported for existing model packages during the migration.

---

## Executive Summary & System Goals

### Vision

Provide a small, stable composition layer for simulations: wire reusable components ("biomodules") into a `BioWorld`, run them with a single orchestration contract, and visualize/debug labs via the local `labs serve` web UI. Biomodules are self-contained Python packages that can wrap external simulators internally (SBML/NeuroML/CellML/etc.) without a separate adapter layer.

### Core Mission

- Compose simulations from reusable, interoperable biomodules.
- Make "run + visualize + share a config" the default workflow (local-first; hosted later).
- Keep the runtime small and predictable while letting biomodules embed their own simulator/tooling.

### Primary Users

- Developers and researchers who need composable simulation workflows and fast iteration.
- Near-term beachhead: neuroscience demos (single neuron + small E/I microcircuits) with strong visuals and reproducible configs.

---

## Installation

Preferred (pinned GitHub ref):

```console
pip install "biosimulant @ git+https://github.com/<org>/biosim.git@<ref>"
```

Alternative (package index):

```console
pip install biosimulant
```

The default install includes the local web runtime used by
`biosimulant labs serve`.

For the shared ONNX biomodule helpers:

```console
pip install "biosimulant[ml]"
```

### Compatibility and command ownership

Use `biosimulant` for new Python examples:

```python
import biosimulant as biosim
```

The package still ships the legacy `biosim` Python import path so existing model
packages keep working:

```python
import biosim
```

Use `biosimulant` for new CLI examples:

```bash
biosimulant --help
python -m biosimulant --help
```

`python -m biosim` remains available as a compatibility command. If a machine
also has the Desktop/product CLI installed, `PATH` decides which `biosimulant`
binary runs. Use `python -m biosimulant ...` to force the Python package CLI.
The Python package owns local open-source workflows; Desktop/product extensions
own Hub, auth, cloud, app state, and managed-service workflows.

### Shell completion

`biosimulant` supports bash and zsh completion for commands, options, and file
paths. Add the completion hook once to your shell startup file:

```bash
# ~/.zshrc or ~/.bashrc
eval "$(register-python-argcomplete biosimulant)"
```

Then restart the shell, or run `source ~/.zshrc` / `source ~/.bashrc`.

## Publishing to PyPI

See the release guide: [`docs/releasing.md`](docs/releasing.md).

## Local Labs

The open-source Python CLI can create, manage, validate, run, and serve local
lab source trees without Desktop or Hub:

```bash
biosimulant labs create ./my-lab --name "My Lab"
biosimulant labs list .
biosimulant labs get ./my-lab
biosimulant labs package ./my-lab --out dist
biosimulant labs validate ./my-lab
biosimulant labs run ./my-lab --no-install-deps
biosimulant labs serve ./my-lab
```

`labs init` creates a runnable starter lab by default. Use `--empty` when you
want only a bare `lab.yaml` scaffold.
Managed local lab identity lives in `.biosimulant/lab.json`; exported labs use
the portable `lab.yaml` source manifest.

## Packaging Models And Labs

`biosimulant` packages labs through the `labs` command group. Standalone model
package helpers remain Python APIs, but the public CLI object is the lab.

Common commands:

```bash
# Validate and build a package repository manifest
biosimulant labs release validate biosimulant-packages.yaml
biosimulant labs release build biosimulant-packages.yaml --out dist/biosimulant-packages

# Build a self-contained lab package (.bsilab)
biosimulant labs package path/to/lab --out dist

# Discover and pull public registry labs
biosimulant labs search immune
biosimulant labs info owner/lab-name@1.0.0
biosimulant labs pull owner/lab-name@1.0.0 --target ./labs/lab-name

# Validate or run a lab source tree or .bsilab
biosimulant labs validate path/to/lab
biosimulant labs run dist/local__source-lab-1.0.0.bsilab --no-install-deps
```

Notes:
- local `labs validate`, `labs run`, and `labs serve` can use source labs without `package:` or `version:`; the CLI assigns a transient `local/<lab-directory-slug>` identity for local execution.
- standalone `labs package` builds require `package:` and `version:` in `lab.yaml`, or explicit `--package` and `--version` values. Release manifests can supply identity through `biosimulant-packages.yaml`.
- model dependencies in manifests must use exact `==` pins.
- lab builds are always self-contained and preserve the full runnable source tree inside the `.bsilab`.
- nested lab dependencies must use relative `path` refs and must already exist inside the packaged lab directory.
- `validate` prints human-readable success or failure output by default; add `--json` for machine-readable output.

See [`docs/packaging.md`](docs/packaging.md) for the full package layout, recommended authoring flow, and CLI examples.

## Provisional Runtime Helpers

`biosimulant.runtime` is the provisional public home for package interpretation helpers shared by the open-source CLI and Biosimulant platform executors. It owns entrypoint loading, typed `runtime.initial_inputs` coercion, communication-step resolution, and source-neutral lab flattening. Import these helpers from `biosimulant.runtime`; the legacy `biosim.runtime` path remains available for compatibility.

## BioModule Convenience Layers

`BioModule` remains the minimal full-control runtime contract. For common model
adapters, `biosimulant` also exports opt-in helpers:

- `SignalEmitterBioModule`: output storage, source-name resolution, and raw
  value to typed `BioSignal` wrapping.
- `StatefulBioModule`: fixed-step window advancement, input override storage,
  bounded history, and output publishing hooks.

Signal helper functions are available from `biosimulant.signals` and top-level
`biosimulant`: `unwrap_payload`, `coerce_float`, `scalar_or_record_input`, and
`make_signal`.

## Examples

- See `examples/` for quick-start scripts. Try:

```bash
pip install -e .
python examples/basic_usage.py
```

For advanced curated demos (neuro/ecology), wiring configs, and model-pack templates, see the companion repo:

- https://github.com/Biosimulant/models

### Quick Start: BioWorld

Minimal usage:

```python
import biosimulant as biosim
from biosimulant import ScalarSignal, SignalSpec


class Counter(biosim.BioModule):
    def __init__(self):
        self.value = 0
        self._t = 0.0

    def outputs(self):
        return {"count": SignalSpec.scalar(dtype="int64", emitted_unit="1")}

    def advance_window(self, start: float, end: float) -> None:
        _ = start
        self.value += 1
        self._t = end

    def get_outputs(self):
        return {
            "count": ScalarSignal(
                source="counter",
                name="count",
                value=self.value,
                emitted_at=self._t,
                spec=self.outputs()["count"],
            )
        }

    def snapshot(self) -> dict:
        return {"value": self.value, "t": self._t}

    def restore(self, snapshot: dict) -> None:
        self.value = int(snapshot.get("value", 0))
        self._t = float(snapshot.get("t", 0.0))


world = biosim.BioWorld(communication_step=0.1)
world.add_biomodule("counter", Counter())
world.run(duration=1.0)
```

Outputs produced during a communication window are committed at the end of that
window and become visible to downstream modules on a later communication turn.
For final report, export, or visualisation modules in workflow-style graphs, call
`world.settle(steps=1)` after `world.run(...)` to propagate final outputs without
advancing simulated time.

### Visuals from Modules

Modules may optionally expose visuals via `visualize()`, returning a dict or list of dicts with keys `render` and `data`. The world can collect them without any transport layer:

```python
class MyModule(biosim.BioModule):
    def advance_window(self, start: float, end: float) -> None:
        _ = start, end

    def get_outputs(self):
        return {}

    def snapshot(self) -> dict:
        return {}

    def restore(self, snapshot: dict) -> None:
        _ = snapshot

    def visualize(self):
        return {
            "render": "timeseries",
            "data": {"series": [{"name": "s", "points": [[0.0, 1.0]]}]},
        }

world = biosim.BioWorld(communication_step=0.1)
world.add_biomodule("module", MyModule())
world.run(duration=0.1)
print(world.collect_visuals())  # [{"module": "module", "visuals": [...]}]
```

If visuals are generated by a separate downstream module wired to another
producer's final outputs, run one or more settle turns before collecting visuals:
`world.run(duration=...); world.settle(1); world.collect_visuals()`.

See `examples/visuals_demo.py` for a minimal end-to-end example.

### ONNX Modules

`biosimulant` can host ONNX-backed modules without changing the core runtime. Install
the ML extras and wrap the ONNX model behind the standard `BioModule`
interface:

```python
from biosimulant import OnnxClassifierModule, ScalarSignal, SignalSpec

classifier = OnnxClassifierModule(
    model_path="artifacts/model.onnx",
    class_labels=["quiescent", "subthreshold", "spiking"],
    input_port="state_vector",
    probabilities_port="state_probabilities",
    predicted_port="predicted_state",
    input_vector_length=4,
)

classifier.set_inputs(
    {
        "state_vector": ScalarSignal(
            source="adapter",
            name="state_vector",
            value=-64.0,
            emitted_at=0.0,
            spec=SignalSpec.scalar(dtype="float64"),
        )
    }
)
classifier.advance_window(0.0, 0.001)
print(classifier.get_outputs()["predicted_state"].value)
```

Model packs can subclass `OnnxClassifierModule` to set model-relative
`model_path`, port names, and label sets while keeping the inference logic in
the shared library.

## Local Lab UI

`biosimulant labs serve` starts the bundled local lab UI from any runnable lab
source tree, `.bsilab` package, or registry reference.

```console
biosimulant labs serve ./my-lab
```

The command opens the browser by default and serves the UI at the root URL, for
example `http://127.0.0.1:8765/`. Use `--no-open` to suppress the browser
launch and `--port` to choose a different port.

The UI stores lab edits in local files:

- `lab.yaml` for model, world, runtime, and wiring changes.
- `wiring-layout.json` for canvas positions.
- Run history is in memory for the active server process.

Maintainer flow for the bundled frontend:

- Edit the private React/Vite app under `packages/labs-serve-ui`.
- Build with `bash scripts/build_labs_serve_ui.sh`.
- Packaging includes `src/biosim/labs_serve/static/**`, so end users never need npm.

### Visual Notes
- VisualSpec types supported now:
  - `timeseries`: `data = { "series": [{ "name": str, "points": [[x, y], ...] }, ...] }`
  - `bar`: `data = { "items": [{ "label": str, "value": number }, ...] }`
  - `scatter`: `data = { "points": [{ "x": number, "y": number, "label"?: str, "series"?: str }, ...] }`
  - `heatmap`: `data = { "values": [[number, ...], ...], "x_labels"?: [str, ...], "y_labels"?: [str, ...] }`
  - `table`: `data = { "columns": [..], "rows": [[..], ...] }` or `data = { "items": [{...}, ...] }`
  - `image`: `data = { "src": str, "alt"?: str, "width"?: number, "height"?: number }`
  - `graph`: simple node-edge graph renderer
  - `structure3d`: `data = { "title"?: str, "source": { "kind": "url", "url": str } | { "kind": "artifact", "artifact_id": str }, "format": "mmcif" | "pdb", "annotations"?: [{ "label": str, "value": str|number|bool }], "initial_view"?: {...} }`
- VisualSpec may also include an optional `description` (string) for hover text or captions.

## Terminology

Understanding the core concepts is essential for working with Biosimulant effectively.

| Term | Description |
|------|-------------|
| **BioWorld** | Runtime container that orchestrates multi-rate biomodules, routes signals, and publishes lifecycle events. |
| **BioModule** | Pluggable unit of behavior with local state. Implements the runnable contract (`setup/reset/advance_to/...`). |
| **BioSignal** | Typed, versioned data payload exchanged between modules via named ports. |
| **WorldEvent** | Runtime events emitted by the BioWorld (`STARTED`, `TICK`, `FINISHED`, etc.). |
| **Wiring** | Module connection graph. Defined programmatically, via `WiringBuilder`, or loaded from YAML/TOML configs. |
| **VisualSpec** | JSON structure returned by `module.visualize()` with `render` type and `data` payload. |

### Event Lifecycle

Every simulation follows this sequence:
```
STARTED -> TICK (xN) -> FINISHED
```

`PAUSED`, `RESUMED`, `STOPPED`, and `ERROR` may also be emitted depending on runtime control flow.

## License

MIT. See `LICENSE.txt`.
