Skip to content

Repository files navigation

Process-Bigraph

PyPI GitHub Pages

Process-Bigraph is a compositional runtime and protocol for building and executing multiscale biological models from interoperable processes.

It provides a shared architectural layer for:

  • declaring process interfaces
  • wiring processes through typed shared state
  • orchestrating execution across heterogeneous timescales
  • supporting dynamic structure (workflows, division, graph rewrites)

Process-Bigraph is the execution core of Vivarium 2.0, designed to integrate models built with different formalismsβ€”including ODEs, FBA, agent-based models, spatial solvers, and machine-learning componentsβ€”into a single coherent simulation.

Process Bigraph composition framework


🧩 What is a Process Bigraph?

A process bigraph combines:

  • Typed stores β€” hierarchical, schema-validated state defined with bigraph-schema
  • Processes β€” executable components with explicit input/output ports
  • Composites β€” encapsulated sub-simulations with their own internal structure
  • Orchestration patterns β€” multi-timestepping, directed workflows, and event-driven rewrites

Processes do not mutate state directly. Instead, they emit typed deltas that are merged by the runtime.

This allows:

  • numerical updates
  • structural rewrites
  • scheduling and orchestration

to coexist under a single execution semantics.

In this sense, Process-Bigraph is a composition protocol, not a domain-specific simulator.


πŸ“„ Paper reference

The conceptual framework and formal semantics of process bigraphs are introduced in:

Agmon, E. & Spangler, R. K.
Process Bigraphs and the Architecture of Compositional Systems Biology
https://arxiv.org/abs/2512.23754


πŸš€ Getting Started

Installation

pip install process-bigraph

Quickstart β€” composites, drafts & templates in code

A runnable five-minute tour of the core concepts. Every block below runs as-is against process-bigraph β‰₯ 1.8.3. Concepts explained in docs/concepts/composites-and-templates.md.

1. A process wired into a composite over a shared store. A composite is a state map of typed nodes; processes couple only by reading/writing shared stores.

from process_bigraph import Composite, Process, allocate_core
from process_bigraph.emitter import emitter_from_wires, gather_emitter_results

class Grow(Process):                                  # a Process = ports + an update
    config_schema = {'rate': 'float'}
    def inputs(self):  return {'level': 'float'}
    def outputs(self): return {'level': 'float'}
    def update(self, state, interval):
        return {'level': state['level'] * self.config['rate'] * interval}  # a delta

core = allocate_core()
core.register_link('Grow', Grow)                      # register it in the local registry

composite = Composite({'state': {
    'level': 1.0,                                     # a shared store
    'grow': {'_type': 'process', 'address': 'local:Grow', 'config': {'rate': 0.5},
             'interval': 1.0, 'inputs': {'level': ['level']}, 'outputs': {'level': ['level']}},
    'emitter': emitter_from_wires({'level': ['level'], 'time': ['global_time']}),
}}, core=core)
composite.run(5.0)
print(gather_emitter_results(composite))
# {('emitter',): [{'level': 1.0, 'time': 0.0}, {'level': 1.5, ...}, ... {'level': 7.59, 'time': 5.0}]}

2. A draft process β€” a present-but-inert placeholder node. It declares a contract (ports + description) but has no update, so a composite containing it still runs; the draft just no-ops. Complementary to a site (step 3): a draft is a node that is there but inert, a site is an empty hole.

from process_bigraph import DraftProcess, draft_process

@draft_process(name="PTH secretion",
    inputs={'ca_sense': 'float'}, outputs={'pth_out': 'float'},
    contract={'summary': 'senses serum Ca, secretes PTH', 'senses': 'calcium', 'makes': 'PTH'})
class PTHSecretion(DraftProcess):
    pass

core.register_link('PTHSecretion', PTHSecretion)
print(PTHSecretion({}, core=core).describe())
# DRAFT β€” senses serum Ca, secretes PTH  Β·  makes: PTH  Β·  senses: calcium  Β·  status: draft - no update dynamics yet

A module-scope draft auto-registers, so it appears in the vivarium-workbench dashboard (Modules β†’ Processes) marked DRAFT with its ports and contract β€” no code change needed. Replace it with a real Process once the mechanism is committed.

3. A template β€” a composite with an open site (hole). A template is a document that isn't ground: it has open sites ({"_type": "site"}). Composite refuses to run one until every required site is filled.

from process_bigraph import Step
from process_bigraph.templates import open_sites, is_ground_document, template_document

class ReportCard(Step):                               # a fixed downstream verdict
    config_schema = {'threshold': 'float'}
    def inputs(self):  return {'level': 'float'}
    def outputs(self): return {'verdict': 'string'}
    def update(self, state):
        return {'verdict': 'pass' if state['level'] >= self.config['threshold'] else 'fail'}

core.register_link('ReportCard', ReportCard)
MODEL_FACE = {'_type': 'link', '_inputs': {'level': 'float'}, '_outputs': {'level': 'float'}}

template = core.access({'study': {                     # analysis fixed, the model is a HOLE
    'level': 1.0, 'verdict': 'string',
    'model':  {'_type': 'site', '_sort': MODEL_FACE},
    'report': {'_type': 'step', 'address': 'local:ReportCard', 'config': {'threshold': 2.0},
               'inputs': {'level': ['level']}, 'outputs': {'verdict': ['verdict']}}}})

print(open_sites(template), is_ground_document(template))   # [('study', 'model')] False

def model(rate):                                      # any conforming composite fits the hole
    return core.access({'_type': 'process', 'address': 'local:Grow', 'config': {'rate': rate},
                        'interval': 1.0, 'inputs': {'level': ['level']}, 'outputs': {'level': ['level']}})

sim = Composite({'state': template_document(core, template, {'study/model': model(0.5)})}, core=core)
sim.run(4.0)
print(sim.state['study']['verdict'])                  # pass  (a slower model β†’ fail)
# template_document(core, template, {}) β†’ ValueError: not ground β€” required site 'study/model'

4. An investigation β€” one site per member study; gating is filling. Fill a member's site to admit it; leave it open and it is pruned from the built document, so a blocked prerequisite simply never runs.

from process_bigraph.templates import investigation_document

study = lambda: {'level': 1.0, 'verdict': 'string',
    'model':  {'_type': 'site', '_sort': MODEL_FACE},
    'report': {'_type': 'step', 'address': 'local:ReportCard', 'config': {'threshold': 2.0},
               'inputs': {'level': ['level']}, 'outputs': {'verdict': ['verdict']}}}

inv = core.access({'investigation': {'study_A': study(), 'study_B': study()}})
document, blocked = investigation_document(
    core, inv, {'investigation/study_A/model': model(0.5)}, member_depth=2)  # fill A only
print(blocked, sorted(document['investigation']))     # ['investigation/study_B'] ['study_A']

This same template/site machinery is what viva-superpowers uses to compile a whole investigation into a composite (one StudyStep per member). The study/investigation layer is documented in viva-superpowers docs/concepts/composites-templates-and-the-study-investigation-stack.md.

πŸ“˜ Tutorials

The Process-Bigraph tutorials are executable Jupyter notebooks, rendered to HTML and published automatically on GitHub Pages.

Learning Path (Featured Tutorials)

More tutorials are added continuously and appear automatically in the index.

Architecture

  • The framework, end to end β€” start here to understand the whole system: what the objects are (documents, sites, handles), how they compose, the higher-order DAG, templates and gating, content-addressed artifacts, the git: protocol, and the laws everything else follows from. docs/architecture.md

Topic Guides

  • Emitters β€” Recording Simulation Results Built-in emitters (RAM, console, JSON, SQLite), how to wire them, retrieve results, store runs long-term, and write your own. docs/emitters.md

  • Tick lifecycle β€” how a step network is ordered and advanced, and how a protocol runtime batches remote dispatch. docs/tick_lifecycle.md

  • Distributed lifecycles β€” running parts of a composite off-process. docs/distributed_lifecycles.md

  • Deploying a Step network to Nextflow β€” opt-in: compile a composite's Step network into a Nextflow DSL2 workflow and run it on a batch backend (local / SLURM). Nothing changes about Composite.run(). docs/nextflow.md


πŸ§ͺ Reference Implementation: spatio-flux

Process-Bigraph is exercised end-to-end in spatio-flux, a multiscale reference model built entirely using the process-bigraph protocol.

spatio-flux composes spatial fields, particle dynamics, and metabolic processes using typed shared state and declarative orchestration.

GitHub: https://github.com/vivarium-collective/spatio-flux
Live test report: https://vivarium-collective.github.io/spatio-flux/report/index.html


πŸ”— Related Resources


πŸ“œ License

Process-Bigraph is open-source software released under the
Apache 2 License.

About

Vivarium 2.0 simulation interfaces

Resources

Code of conduct

Contributing

Stars

9 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages