nomass 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nomass-1.0.0/LICENSE +21 -0
- nomass-1.0.0/PKG-INFO +233 -0
- nomass-1.0.0/README.md +193 -0
- nomass-1.0.0/nomass/__init__.py +29 -0
- nomass-1.0.0/nomass/distributions.py +437 -0
- nomass-1.0.0/nomass/simulation.py +968 -0
- nomass-1.0.0/nomass.egg-info/PKG-INFO +233 -0
- nomass-1.0.0/nomass.egg-info/SOURCES.txt +12 -0
- nomass-1.0.0/nomass.egg-info/dependency_links.txt +1 -0
- nomass-1.0.0/nomass.egg-info/requires.txt +12 -0
- nomass-1.0.0/nomass.egg-info/top_level.txt +1 -0
- nomass-1.0.0/pyproject.toml +56 -0
- nomass-1.0.0/setup.cfg +4 -0
- nomass-1.0.0/tests/test_nomass.py +267 -0
nomass-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Aurélien PELISSIER
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
nomass-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nomass
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Non-Markovian Stochastic Simulation: Gillespie-based simulation of reaction networks with arbitrary inter-event time distributions
|
|
5
|
+
Author-email: Aurélien Pélissier <aurelien.pelissier.38@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/AI-SysBio/NoMaSS
|
|
8
|
+
Project-URL: Documentation, https://github.com/AI-SysBio/NoMaSS/blob/main/docs/api.md
|
|
9
|
+
Project-URL: Repository, https://github.com/AI-SysBio/NoMaSS
|
|
10
|
+
Project-URL: Issues, https://github.com/AI-SysBio/NoMaSS/issues
|
|
11
|
+
Project-URL: Paper, https://doi.org/10.1093/bib/bbag486
|
|
12
|
+
Keywords: gillespie,stochastic simulation,non-markovian,renewal process,systems biology,reaction network,delay,laplace gillespie,mosaic
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
25
|
+
Classifier: Operating System :: OS Independent
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: numpy>=1.21
|
|
30
|
+
Requires-Dist: scipy>=1.7
|
|
31
|
+
Requires-Dist: matplotlib>=3.4
|
|
32
|
+
Provides-Extra: test
|
|
33
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
34
|
+
Provides-Extra: paper
|
|
35
|
+
Requires-Dist: pandas; extra == "paper"
|
|
36
|
+
Requires-Dist: seaborn; extra == "paper"
|
|
37
|
+
Requires-Dist: openpyxl; extra == "paper"
|
|
38
|
+
Requires-Dist: SALib; extra == "paper"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# NoMaSS: Non-Markovian Stochastic Simulation
|
|
42
|
+
|
|
43
|
+
[](https://doi.org/10.1093/bib/bbag486)
|
|
44
|
+
[](LICENSE)
|
|
45
|
+
[](https://pypi.org/project/nomass/)
|
|
46
|
+
|
|
47
|
+
<img align="right" src="https://raw.githubusercontent.com/AI-SysBio/NoMaSS/main/figures/NoMaSS.png" width=380>
|
|
48
|
+
|
|
49
|
+
Discrete stochastic processes are widespread in nature and in human-made systems, with applications in physics, biochemistry, epidemiology, social dynamics and finance. In most of these systems the dynamics cannot be described with memoryless (Markovian) interactions, and simulating them requires dedicated numerical tools.
|
|
50
|
+
|
|
51
|
+
NoMaSS is a practical and scalable Gillespie-based framework to simulate non-Markovian reaction networks with arbitrary inter-event time distributions. It implements three exact methods that can be mixed freely in the same model, one per reaction channel:
|
|
52
|
+
|
|
53
|
+
* **MOSAIC** (rejection based, [1, 2]): propose events at an upper bound of the rate and accept them with the ratio of the true hazard to the bound.
|
|
54
|
+
* **Laplace Gillespie** [3]: give every reactant a Poisson rate drawn from the mixing density of its waiting-time distribution.
|
|
55
|
+
* **DelaySSA** [4]: draw the waiting time when a reactant enters a channel and schedule the event in a priority queue.
|
|
56
|
+
|
|
57
|
+
The methods, their accuracy and their speed are described in the accompanying paper [1].
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
NoMaSS is deliberately lightweight: the efficiency of the framework lies in the algorithms rather than in compiled extensions, so the engine is pure Python and relies only on the standard scientific stack. Python 3.9 or later is required.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pip install nomass
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To work on the code or run the scripts of the paper, clone the repository and install it in editable mode:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git clone https://github.com/AI-SysBio/NoMaSS.git
|
|
73
|
+
cd NoMaSS
|
|
74
|
+
pip install -e .[test,paper]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Check that everything works with
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python examples/toy_example.py
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Quick start
|
|
84
|
+
|
|
85
|
+
The example below ([examples/toy_example.py](examples/toy_example.py)) simulates a small network where each channel uses a different method:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
import nomass
|
|
89
|
+
|
|
90
|
+
# Reaction channels: one waiting-time distribution and one method each
|
|
91
|
+
r1 = nomass.Reaction_channel('A -> B', rate=1, shape_param=20, distribution='Gamma', method='Delay')
|
|
92
|
+
r2 = nomass.Reaction_channel('B -> C + A', rate=4, shape_param=5, distribution='Weibull', method='MOSAIC')
|
|
93
|
+
r3 = nomass.Reaction_channel('C -> 0', rate=0.03, shape_param=0.5, distribution='Gamma', method='Laplace')
|
|
94
|
+
r4 = nomass.Reaction_channel('A + B -> 0', rate=0.03) # two reactants: Poisson process
|
|
95
|
+
|
|
96
|
+
# Initial population (every species must be listed)
|
|
97
|
+
N_init = {'A': 300, 'B': 0, 'C': 0}
|
|
98
|
+
|
|
99
|
+
# Build and run the model
|
|
100
|
+
sim = nomass.Gillespie_simulation(N_init, [r1, r2, r3, r4],
|
|
101
|
+
Tend=10, # length of the simulation
|
|
102
|
+
N_simulations=20, # number of independent realisations
|
|
103
|
+
timepoints=100, # number of recorded time points
|
|
104
|
+
unit='h') # time unit (plots only)
|
|
105
|
+
populations = sim.run_simulations() # array (N_simulations, timepoints, species + total)
|
|
106
|
+
|
|
107
|
+
# Plot the results
|
|
108
|
+
sim.plot_inter_event_time_distribution()
|
|
109
|
+
sim.plot_populations()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The simulation runs in a few seconds and produces the figures below (pass `verbose=False` to silence the progress output).
|
|
113
|
+
|
|
114
|
+
<p align="center">
|
|
115
|
+
<img src="https://raw.githubusercontent.com/AI-SysBio/NoMaSS/main/figures/toy_example.png" width=1000>
|
|
116
|
+
</p>
|
|
117
|
+
|
|
118
|
+
The oscillations created by the nearly deterministic Gamma waiting times are clearly visible. The *theoretical* distributions do not match the *simulated* ones exactly, even with many realisations, and this is expected: species A and B are reactants of two channels at the same time, and the theoretical curve is the distribution the channel would have if it were the only process acting on its reactant. The accuracy of each method is demonstrated in [1] and [2].
|
|
119
|
+
|
|
120
|
+
Reactions are written as strings: `'A + B -> C'`, `'0 -> A'` (creation), `'A -> 0'` (degradation), `'2 A -> B'` (stoichiometry). The plotting methods return their matplotlib figure and axes, so pass `show=False` to customise a figure before displaying it.
|
|
121
|
+
|
|
122
|
+
## Choosing a method
|
|
123
|
+
|
|
124
|
+
| Method | Set with | Best for | Limitations |
|
|
125
|
+
| --- | --- | --- | --- |
|
|
126
|
+
| MOSAIC (rejection) | `method='MOSAIC'` | Large populations, state-dependent rates, any distribution with a bounded hazard | Hazard must be finite at t = 0 (Gamma and Weibull need a shape ≥ 1) |
|
|
127
|
+
| Laplace Gillespie | `method='Laplace'` | Heavy-tailed or decreasing hazards (Gamma/Weibull with shape < 1, Lomax), state-dependent rates | Only distributions whose survival function is a mixture of exponentials |
|
|
128
|
+
| DelaySSA | `method='Delay'` | Any distribution when rates are constant | Waiting times are frozen when drawn, so rates that change over time are not followed |
|
|
129
|
+
|
|
130
|
+
When `method` is omitted, NoMaSS uses MOSAIC if the hazard is bounded and Laplace Gillespie otherwise. Channels with zero or several reactants are always Poisson processes with propensity `rate × product of the reactant counts`, because the waiting time of an individual is not defined in that case.
|
|
131
|
+
|
|
132
|
+
A Gamma waiting time with an integer shape can also be embedded in a chain of exponential stages (hidden Markov embedding); [examples/erlang_embedding.py](examples/erlang_embedding.py) compares the two descriptions.
|
|
133
|
+
|
|
134
|
+
## Supported distributions
|
|
135
|
+
|
|
136
|
+
| Distribution | MOSAIC | Laplace | DelaySSA |
|
|
137
|
+
| --- | :---: | :---: | :---: |
|
|
138
|
+
| Exponential | ✅ | ✅ | ✅ |
|
|
139
|
+
| Gamma | ✅ (α ≥ 1) | ✅ (α ≤ 1) | ✅ |
|
|
140
|
+
| Weibull | ✅ (α ≥ 1) | ✅ (α ≤ 1) | ✅ |
|
|
141
|
+
| Gaussian / Normal | ✅ | ❌ | ✅ |
|
|
142
|
+
| Lognormal | ✅ | ❌ | ✅ |
|
|
143
|
+
| Cauchy | ✅ | ❌ | ✅ |
|
|
144
|
+
| Power law, Pareto type I | ✅ | ❌ | ✅ |
|
|
145
|
+
| Power law, Lomax (Pareto type II) | ✅ | ✅ | ✅ |
|
|
146
|
+
|
|
147
|
+
Every distribution is parameterised by a nominal `rate` λ, the inverse of the mean waiting time (or of the median when the mean is undefined), and a dimensionless `shape_param` α (full details in Table S1 of [1]):
|
|
148
|
+
|
|
149
|
+
| Distribution | `rate` | `shape_param` |
|
|
150
|
+
| --- | --- | --- |
|
|
151
|
+
| Exponential | 1 / mean | unused |
|
|
152
|
+
| Gamma | 1 / mean | shape α |
|
|
153
|
+
| Weibull | 1 / mean | shape k |
|
|
154
|
+
| Gaussian | 1 / mean | std / mean |
|
|
155
|
+
| Lognormal | 1 / mean | std / mean |
|
|
156
|
+
| Cauchy | 1 / median | scale / median |
|
|
157
|
+
| Pareto type I | 1 / median | tail index α |
|
|
158
|
+
| Lomax | 1 / median | tail index α |
|
|
159
|
+
|
|
160
|
+
Gaussian and Cauchy waiting times are truncated to non-negative values. Aliases such as `'exp'`, `'norm'`, `'lognorm'` and `'weib'` are accepted. Requesting a combination that a method does not support raises an error at channel creation. [examples/all_distributions.py](examples/all_distributions.py) runs a cycle through all eight distributions.
|
|
161
|
+
|
|
162
|
+
## State-dependent waiting times
|
|
163
|
+
|
|
164
|
+
Pass `update_function` to change the `rate` or `shape_param` of any channel after every iteration, for example to implement feedback or resource limitation ([examples/state_dependent_rates.py](examples/state_dependent_rates.py)):
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
def update_rates(sim):
|
|
168
|
+
N = sim.reactant_population['Cell']
|
|
169
|
+
sim.reaction_channel_list[0].rate = r0 * (1 - N / K) # logistic growth
|
|
170
|
+
|
|
171
|
+
sim = nomass.Gillespie_simulation({'Cell': 5}, [division], Tend=25, update_function=update_rates)
|
|
172
|
+
populations = sim.run_simulations()
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
MOSAIC and Laplace Gillespie follow such changes exactly (Laplace rates are rescaled when the rate changes and redrawn when the shape changes). Delay-based channels draw their waiting time once, when the reactant enters the channel, so they cannot follow parameters that change afterwards: avoid `method='Delay'` for rapidly changing systems.
|
|
176
|
+
|
|
177
|
+
## Individual properties
|
|
178
|
+
|
|
179
|
+
Every individual is a `nomass.Reactant` object that can carry arbitrary attributes. The `on_reaction` hook is called after each accepted reaction with the consumed and produced individuals, which is where properties are set or inherited ([examples/individual_properties.py](examples/individual_properties.py)):
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
def inherit(sim, channel, consumed, produced):
|
|
183
|
+
if channel is division:
|
|
184
|
+
for daughter in produced:
|
|
185
|
+
daughter.generation = consumed[0].generation + 1
|
|
186
|
+
|
|
187
|
+
sim = nomass.Gillespie_simulation({'Cell': 20}, [division, death], Tend=6, on_reaction=inherit)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Set `transfer_identity=True` on a channel such as `'Gon -> Gon + mRNA'` to keep the same individual (with its properties and the clocks of its other channels) instead of replacing it. For kinetic laws that are not proportional to the reactant counts, subclass `Gillespie_simulation` and override `compute_propensities`; the full API is described in [docs/api.md](docs/api.md), and the hazards, samplers and Laplace mixing densities of all distributions are in [nomass/distributions.py](nomass/distributions.py).
|
|
191
|
+
|
|
192
|
+
## Repository layout
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
nomass/ the engine (distributions and simulation)
|
|
196
|
+
docs/ API reference (docs/api.md)
|
|
197
|
+
examples/ short runnable examples
|
|
198
|
+
paper/ scripts, data and precomputed results reproducing the paper's figures
|
|
199
|
+
tests/ pytest suite (pytest -q)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The biochemical systems of the paper (bacterial growth, stem cell differentiation and RNA transcription) live in `paper/`, with a figure-to-script table in [paper/README.md](paper/README.md) and the article with its supplementary material in [paper/Pelissier2026_NoMaSS_main_and_SI.pdf](paper/Pelissier2026_NoMaSS_main_and_SI.pdf).
|
|
203
|
+
|
|
204
|
+
## Citation
|
|
205
|
+
|
|
206
|
+
If you use NoMaSS, please cite (full text with supplementary material: [paper/Pelissier2026_NoMaSS_main_and_SI.pdf](paper/Pelissier2026_NoMaSS_main_and_SI.pdf))
|
|
207
|
+
|
|
208
|
+
> Pélissier A., Phan M., Beerenwinkel N., Rodríguez Martínez M. *Gillespie-based simulation and inference for non-Markovian stochastic reaction networks.* Briefings in Bioinformatics 27(5), bbag486 (2026). doi:[10.1093/bib/bbag486](https://doi.org/10.1093/bib/bbag486)
|
|
209
|
+
|
|
210
|
+
```bibtex
|
|
211
|
+
@article{pelissier2026nomass,
|
|
212
|
+
title = {Gillespie-based simulation and inference for non-Markovian stochastic reaction networks},
|
|
213
|
+
author = {P{\'e}lissier, Aur{\'e}lien and Phan, Miroslav and Beerenwinkel, Niko and Rodr{\'i}guez Mart{\'i}nez, Mar{\'i}a},
|
|
214
|
+
journal = {Briefings in Bioinformatics},
|
|
215
|
+
volume = {27},
|
|
216
|
+
number = {5},
|
|
217
|
+
pages = {bbag486},
|
|
218
|
+
year = {2026},
|
|
219
|
+
doi = {10.1093/bib/bbag486}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
*Questions are welcome, feel free to open an issue or drop me an email.*
|
|
224
|
+
|
|
225
|
+
## References
|
|
226
|
+
|
|
227
|
+
[1] Pélissier A., Phan M., Beerenwinkel N., Rodríguez Martínez M. Gillespie-based simulation and inference for non-Markovian stochastic reaction networks. *Briefings in Bioinformatics* **27**(5), bbag486 (2026). doi:[10.1093/bib/bbag486](https://doi.org/10.1093/bib/bbag486)
|
|
228
|
+
|
|
229
|
+
[2] Pélissier A., Phan M., Le Bail D., Beerenwinkel N., Rodríguez Martínez M. Unifying non-Markovian dynamics and agent heterogeneity in scalable stochastic networks. *Nature Communications* **17**, 3345 (2026). doi:[10.1038/s41467-026-69817-y](https://doi.org/10.1038/s41467-026-69817-y)
|
|
230
|
+
|
|
231
|
+
[3] Masuda N., Rocha L. E. C. A Gillespie algorithm for non-Markovian stochastic processes. *SIAM Review* **60**(1), 95-115 (2018). doi:[10.1137/16M1055876](https://doi.org/10.1137/16M1055876)
|
|
232
|
+
|
|
233
|
+
[4] Anderson D. F. A modified next reaction method for simulating chemical systems with time dependent propensities and delays. *The Journal of Chemical Physics* **127**(21), 214107 (2007). doi:[10.1063/1.2799998](https://doi.org/10.1063/1.2799998)
|
nomass-1.0.0/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# NoMaSS: Non-Markovian Stochastic Simulation
|
|
2
|
+
|
|
3
|
+
[](https://doi.org/10.1093/bib/bbag486)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://pypi.org/project/nomass/)
|
|
6
|
+
|
|
7
|
+
<img align="right" src="https://raw.githubusercontent.com/AI-SysBio/NoMaSS/main/figures/NoMaSS.png" width=380>
|
|
8
|
+
|
|
9
|
+
Discrete stochastic processes are widespread in nature and in human-made systems, with applications in physics, biochemistry, epidemiology, social dynamics and finance. In most of these systems the dynamics cannot be described with memoryless (Markovian) interactions, and simulating them requires dedicated numerical tools.
|
|
10
|
+
|
|
11
|
+
NoMaSS is a practical and scalable Gillespie-based framework to simulate non-Markovian reaction networks with arbitrary inter-event time distributions. It implements three exact methods that can be mixed freely in the same model, one per reaction channel:
|
|
12
|
+
|
|
13
|
+
* **MOSAIC** (rejection based, [1, 2]): propose events at an upper bound of the rate and accept them with the ratio of the true hazard to the bound.
|
|
14
|
+
* **Laplace Gillespie** [3]: give every reactant a Poisson rate drawn from the mixing density of its waiting-time distribution.
|
|
15
|
+
* **DelaySSA** [4]: draw the waiting time when a reactant enters a channel and schedule the event in a priority queue.
|
|
16
|
+
|
|
17
|
+
The methods, their accuracy and their speed are described in the accompanying paper [1].
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
NoMaSS is deliberately lightweight: the efficiency of the framework lies in the algorithms rather than in compiled extensions, so the engine is pure Python and relies only on the standard scientific stack. Python 3.9 or later is required.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install nomass
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To work on the code or run the scripts of the paper, clone the repository and install it in editable mode:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/AI-SysBio/NoMaSS.git
|
|
33
|
+
cd NoMaSS
|
|
34
|
+
pip install -e .[test,paper]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Check that everything works with
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python examples/toy_example.py
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
The example below ([examples/toy_example.py](examples/toy_example.py)) simulates a small network where each channel uses a different method:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import nomass
|
|
49
|
+
|
|
50
|
+
# Reaction channels: one waiting-time distribution and one method each
|
|
51
|
+
r1 = nomass.Reaction_channel('A -> B', rate=1, shape_param=20, distribution='Gamma', method='Delay')
|
|
52
|
+
r2 = nomass.Reaction_channel('B -> C + A', rate=4, shape_param=5, distribution='Weibull', method='MOSAIC')
|
|
53
|
+
r3 = nomass.Reaction_channel('C -> 0', rate=0.03, shape_param=0.5, distribution='Gamma', method='Laplace')
|
|
54
|
+
r4 = nomass.Reaction_channel('A + B -> 0', rate=0.03) # two reactants: Poisson process
|
|
55
|
+
|
|
56
|
+
# Initial population (every species must be listed)
|
|
57
|
+
N_init = {'A': 300, 'B': 0, 'C': 0}
|
|
58
|
+
|
|
59
|
+
# Build and run the model
|
|
60
|
+
sim = nomass.Gillespie_simulation(N_init, [r1, r2, r3, r4],
|
|
61
|
+
Tend=10, # length of the simulation
|
|
62
|
+
N_simulations=20, # number of independent realisations
|
|
63
|
+
timepoints=100, # number of recorded time points
|
|
64
|
+
unit='h') # time unit (plots only)
|
|
65
|
+
populations = sim.run_simulations() # array (N_simulations, timepoints, species + total)
|
|
66
|
+
|
|
67
|
+
# Plot the results
|
|
68
|
+
sim.plot_inter_event_time_distribution()
|
|
69
|
+
sim.plot_populations()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The simulation runs in a few seconds and produces the figures below (pass `verbose=False` to silence the progress output).
|
|
73
|
+
|
|
74
|
+
<p align="center">
|
|
75
|
+
<img src="https://raw.githubusercontent.com/AI-SysBio/NoMaSS/main/figures/toy_example.png" width=1000>
|
|
76
|
+
</p>
|
|
77
|
+
|
|
78
|
+
The oscillations created by the nearly deterministic Gamma waiting times are clearly visible. The *theoretical* distributions do not match the *simulated* ones exactly, even with many realisations, and this is expected: species A and B are reactants of two channels at the same time, and the theoretical curve is the distribution the channel would have if it were the only process acting on its reactant. The accuracy of each method is demonstrated in [1] and [2].
|
|
79
|
+
|
|
80
|
+
Reactions are written as strings: `'A + B -> C'`, `'0 -> A'` (creation), `'A -> 0'` (degradation), `'2 A -> B'` (stoichiometry). The plotting methods return their matplotlib figure and axes, so pass `show=False` to customise a figure before displaying it.
|
|
81
|
+
|
|
82
|
+
## Choosing a method
|
|
83
|
+
|
|
84
|
+
| Method | Set with | Best for | Limitations |
|
|
85
|
+
| --- | --- | --- | --- |
|
|
86
|
+
| MOSAIC (rejection) | `method='MOSAIC'` | Large populations, state-dependent rates, any distribution with a bounded hazard | Hazard must be finite at t = 0 (Gamma and Weibull need a shape ≥ 1) |
|
|
87
|
+
| Laplace Gillespie | `method='Laplace'` | Heavy-tailed or decreasing hazards (Gamma/Weibull with shape < 1, Lomax), state-dependent rates | Only distributions whose survival function is a mixture of exponentials |
|
|
88
|
+
| DelaySSA | `method='Delay'` | Any distribution when rates are constant | Waiting times are frozen when drawn, so rates that change over time are not followed |
|
|
89
|
+
|
|
90
|
+
When `method` is omitted, NoMaSS uses MOSAIC if the hazard is bounded and Laplace Gillespie otherwise. Channels with zero or several reactants are always Poisson processes with propensity `rate × product of the reactant counts`, because the waiting time of an individual is not defined in that case.
|
|
91
|
+
|
|
92
|
+
A Gamma waiting time with an integer shape can also be embedded in a chain of exponential stages (hidden Markov embedding); [examples/erlang_embedding.py](examples/erlang_embedding.py) compares the two descriptions.
|
|
93
|
+
|
|
94
|
+
## Supported distributions
|
|
95
|
+
|
|
96
|
+
| Distribution | MOSAIC | Laplace | DelaySSA |
|
|
97
|
+
| --- | :---: | :---: | :---: |
|
|
98
|
+
| Exponential | ✅ | ✅ | ✅ |
|
|
99
|
+
| Gamma | ✅ (α ≥ 1) | ✅ (α ≤ 1) | ✅ |
|
|
100
|
+
| Weibull | ✅ (α ≥ 1) | ✅ (α ≤ 1) | ✅ |
|
|
101
|
+
| Gaussian / Normal | ✅ | ❌ | ✅ |
|
|
102
|
+
| Lognormal | ✅ | ❌ | ✅ |
|
|
103
|
+
| Cauchy | ✅ | ❌ | ✅ |
|
|
104
|
+
| Power law, Pareto type I | ✅ | ❌ | ✅ |
|
|
105
|
+
| Power law, Lomax (Pareto type II) | ✅ | ✅ | ✅ |
|
|
106
|
+
|
|
107
|
+
Every distribution is parameterised by a nominal `rate` λ, the inverse of the mean waiting time (or of the median when the mean is undefined), and a dimensionless `shape_param` α (full details in Table S1 of [1]):
|
|
108
|
+
|
|
109
|
+
| Distribution | `rate` | `shape_param` |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| Exponential | 1 / mean | unused |
|
|
112
|
+
| Gamma | 1 / mean | shape α |
|
|
113
|
+
| Weibull | 1 / mean | shape k |
|
|
114
|
+
| Gaussian | 1 / mean | std / mean |
|
|
115
|
+
| Lognormal | 1 / mean | std / mean |
|
|
116
|
+
| Cauchy | 1 / median | scale / median |
|
|
117
|
+
| Pareto type I | 1 / median | tail index α |
|
|
118
|
+
| Lomax | 1 / median | tail index α |
|
|
119
|
+
|
|
120
|
+
Gaussian and Cauchy waiting times are truncated to non-negative values. Aliases such as `'exp'`, `'norm'`, `'lognorm'` and `'weib'` are accepted. Requesting a combination that a method does not support raises an error at channel creation. [examples/all_distributions.py](examples/all_distributions.py) runs a cycle through all eight distributions.
|
|
121
|
+
|
|
122
|
+
## State-dependent waiting times
|
|
123
|
+
|
|
124
|
+
Pass `update_function` to change the `rate` or `shape_param` of any channel after every iteration, for example to implement feedback or resource limitation ([examples/state_dependent_rates.py](examples/state_dependent_rates.py)):
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
def update_rates(sim):
|
|
128
|
+
N = sim.reactant_population['Cell']
|
|
129
|
+
sim.reaction_channel_list[0].rate = r0 * (1 - N / K) # logistic growth
|
|
130
|
+
|
|
131
|
+
sim = nomass.Gillespie_simulation({'Cell': 5}, [division], Tend=25, update_function=update_rates)
|
|
132
|
+
populations = sim.run_simulations()
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
MOSAIC and Laplace Gillespie follow such changes exactly (Laplace rates are rescaled when the rate changes and redrawn when the shape changes). Delay-based channels draw their waiting time once, when the reactant enters the channel, so they cannot follow parameters that change afterwards: avoid `method='Delay'` for rapidly changing systems.
|
|
136
|
+
|
|
137
|
+
## Individual properties
|
|
138
|
+
|
|
139
|
+
Every individual is a `nomass.Reactant` object that can carry arbitrary attributes. The `on_reaction` hook is called after each accepted reaction with the consumed and produced individuals, which is where properties are set or inherited ([examples/individual_properties.py](examples/individual_properties.py)):
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
def inherit(sim, channel, consumed, produced):
|
|
143
|
+
if channel is division:
|
|
144
|
+
for daughter in produced:
|
|
145
|
+
daughter.generation = consumed[0].generation + 1
|
|
146
|
+
|
|
147
|
+
sim = nomass.Gillespie_simulation({'Cell': 20}, [division, death], Tend=6, on_reaction=inherit)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Set `transfer_identity=True` on a channel such as `'Gon -> Gon + mRNA'` to keep the same individual (with its properties and the clocks of its other channels) instead of replacing it. For kinetic laws that are not proportional to the reactant counts, subclass `Gillespie_simulation` and override `compute_propensities`; the full API is described in [docs/api.md](docs/api.md), and the hazards, samplers and Laplace mixing densities of all distributions are in [nomass/distributions.py](nomass/distributions.py).
|
|
151
|
+
|
|
152
|
+
## Repository layout
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
nomass/ the engine (distributions and simulation)
|
|
156
|
+
docs/ API reference (docs/api.md)
|
|
157
|
+
examples/ short runnable examples
|
|
158
|
+
paper/ scripts, data and precomputed results reproducing the paper's figures
|
|
159
|
+
tests/ pytest suite (pytest -q)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The biochemical systems of the paper (bacterial growth, stem cell differentiation and RNA transcription) live in `paper/`, with a figure-to-script table in [paper/README.md](paper/README.md) and the article with its supplementary material in [paper/Pelissier2026_NoMaSS_main_and_SI.pdf](paper/Pelissier2026_NoMaSS_main_and_SI.pdf).
|
|
163
|
+
|
|
164
|
+
## Citation
|
|
165
|
+
|
|
166
|
+
If you use NoMaSS, please cite (full text with supplementary material: [paper/Pelissier2026_NoMaSS_main_and_SI.pdf](paper/Pelissier2026_NoMaSS_main_and_SI.pdf))
|
|
167
|
+
|
|
168
|
+
> Pélissier A., Phan M., Beerenwinkel N., Rodríguez Martínez M. *Gillespie-based simulation and inference for non-Markovian stochastic reaction networks.* Briefings in Bioinformatics 27(5), bbag486 (2026). doi:[10.1093/bib/bbag486](https://doi.org/10.1093/bib/bbag486)
|
|
169
|
+
|
|
170
|
+
```bibtex
|
|
171
|
+
@article{pelissier2026nomass,
|
|
172
|
+
title = {Gillespie-based simulation and inference for non-Markovian stochastic reaction networks},
|
|
173
|
+
author = {P{\'e}lissier, Aur{\'e}lien and Phan, Miroslav and Beerenwinkel, Niko and Rodr{\'i}guez Mart{\'i}nez, Mar{\'i}a},
|
|
174
|
+
journal = {Briefings in Bioinformatics},
|
|
175
|
+
volume = {27},
|
|
176
|
+
number = {5},
|
|
177
|
+
pages = {bbag486},
|
|
178
|
+
year = {2026},
|
|
179
|
+
doi = {10.1093/bib/bbag486}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
*Questions are welcome, feel free to open an issue or drop me an email.*
|
|
184
|
+
|
|
185
|
+
## References
|
|
186
|
+
|
|
187
|
+
[1] Pélissier A., Phan M., Beerenwinkel N., Rodríguez Martínez M. Gillespie-based simulation and inference for non-Markovian stochastic reaction networks. *Briefings in Bioinformatics* **27**(5), bbag486 (2026). doi:[10.1093/bib/bbag486](https://doi.org/10.1093/bib/bbag486)
|
|
188
|
+
|
|
189
|
+
[2] Pélissier A., Phan M., Le Bail D., Beerenwinkel N., Rodríguez Martínez M. Unifying non-Markovian dynamics and agent heterogeneity in scalable stochastic networks. *Nature Communications* **17**, 3345 (2026). doi:[10.1038/s41467-026-69817-y](https://doi.org/10.1038/s41467-026-69817-y)
|
|
190
|
+
|
|
191
|
+
[3] Masuda N., Rocha L. E. C. A Gillespie algorithm for non-Markovian stochastic processes. *SIAM Review* **60**(1), 95-115 (2018). doi:[10.1137/16M1055876](https://doi.org/10.1137/16M1055876)
|
|
192
|
+
|
|
193
|
+
[4] Anderson D. F. A modified next reaction method for simulating chemical systems with time dependent propensities and delays. *The Journal of Chemical Physics* **127**(21), 214107 (2007). doi:[10.1063/1.2799998](https://doi.org/10.1063/1.2799998)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NoMaSS: Non-Markovian Stochastic Simulation.
|
|
3
|
+
|
|
4
|
+
A Gillespie-based framework to simulate reaction networks whose inter-event
|
|
5
|
+
times follow arbitrary distributions, mixing three exact methods per reaction
|
|
6
|
+
channel: MOSAIC (rejection), Laplace Gillespie and DelaySSA.
|
|
7
|
+
|
|
8
|
+
Quick start::
|
|
9
|
+
|
|
10
|
+
import nomass
|
|
11
|
+
|
|
12
|
+
r1 = nomass.Reaction_channel('A -> B', rate=1, shape_param=20, distribution='Gamma')
|
|
13
|
+
sim = nomass.Gillespie_simulation({'A': 300, 'B': 0}, [r1], Tend=10, N_simulations=20, unit='h')
|
|
14
|
+
populations = sim.run_simulations()
|
|
15
|
+
sim.plot_populations()
|
|
16
|
+
|
|
17
|
+
Reference: Pelissier A., Phan M., Beerenwinkel N., Rodriguez Martinez M.
|
|
18
|
+
"Gillespie-based simulation and inference for non-Markovian stochastic reaction
|
|
19
|
+
networks", Briefings in Bioinformatics 27(5), bbag486 (2026). doi:10.1093/bib/bbag486
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from .simulation import Reaction_channel, Reactant, Gillespie_simulation, parse_reaction, PALETTE
|
|
23
|
+
from .distributions import DISTRIBUTIONS, METHODS
|
|
24
|
+
from . import distributions
|
|
25
|
+
|
|
26
|
+
__version__ = '1.0.0'
|
|
27
|
+
|
|
28
|
+
__all__ = ['Reaction_channel', 'Reactant', 'Gillespie_simulation', 'parse_reaction',
|
|
29
|
+
'distributions', 'DISTRIBUTIONS', 'METHODS', 'PALETTE', '__version__']
|