Skip to content

Commit

Permalink
Merge branch 'release/1.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
harfel committed Aug 10, 2018
2 parents a4dc5fc + e275f6c commit 4e464a0
Show file tree
Hide file tree
Showing 70 changed files with 4,984 additions and 321 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
*.pyc
build/
dist/
validation_data/
stocal.egg-info/
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [1.2] - 2018-08-10

### Added
- Added Gibson & Bruck's NextReactionMethod
- Added Cao et al's tau leaping method in stocal.experimental.tauleap
- Added statistical validation suite in stocal.examples.validation and stocal.samples.dsmts
- Added modular trajectory sampling interface stocal.experimental.samplers
- Added flattening of rule-based processes into static processes
- StochasticSimulationAlgorithm instances accept an optional random seed

### Deprecated
- Deprecated AndersonNRM in favour of AndersonMethod
- Deprecated TrajectorySampler in favour of StochasticSimulationAlgorithm
- Deprecated ReactionRule in favour of TransitionRule
- Deprecated Process.trajectory in favour of Process.sample

### Changed
- TrajectorySampler instances now use an internal random number generator (sampler.rng) rather than python's global one.
- Improved performance of DirectMethod and AndersonMethod
- Improved performance of transition inference in TransitionRule

## [1.1.2] - 2018-05-23

### Fixed
Expand All @@ -18,6 +39,7 @@
### Changed
- MassAction.__repr__ now also prints rate constant


## [1.1] - 2018-03-08

### Fixed
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
include README.md
include doc/*
include stocal/examples/dsmts/*.csv
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ process = stocal.Process([

# Sample a stochastic trajectory of the process
initial_state = {}
trajectory = process.trajectory(initial_state, tmax=100) :
trajectory = process.sample(initial_state, tmax=100) :
for dt, transitions in trajectory:
print trajectory.time, trajectory.state['A2']
```

Expand Down
29 changes: 20 additions & 9 deletions TODO
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
* stocal.algorithms.AndersonMethod.update_state is broken
* stocal.experimental.samplers do not chain as claimed
+ support for proper delay events (-> feature/events)
= release/1.3
- remove dict support in MassAction.reactions and TransitionRule.infer_transitions
- remove invalid-name arguments
- remove AndersonNRM
- remove TrajectorySampler
- remove ReactionRule
- remove Process.trajectory
- allow species equals 0 in initial state?
- regroup tests into specifications, unittests and bugs
- move stocal.experimental.CaoMethod into stocal.algorithms
= release/2.0
- utilities to simplify scipy/matplotlib interaction
- persistency support (save and resume simulations)
- arithmetic operations for Process
- string rewrite rule support
- support for proper delay events
- other kinetic laws, e.g. Hill function
- libSBML integration
- statistical verification tests
- every/until filters and other stop criteria
- curried reactions (determine products only upon application)
- other kinetic laws, e.g. Hill function
- SSA profiling and algorithm variations
- C/C++/D implementation
= version 1.2+
- deprecate dict support in MassAction.reactions and ReactionRule.infer_transitions
- deprecate invalid-name arguments
= version 2
= version 2.1+
= version 3
22 changes: 20 additions & 2 deletions doc/developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ If in doubt, pylint decides.
The test suite for stocal is contained in `stocal.tests` and can be
run using
```bash
python setup.py test
python3 setup.py test
python -m unittest discover stocal.tests
python3 -m unittest discover stocal.tests
```

Passing of all tests is an enforced requirement for all code merged
Expand All @@ -50,13 +50,31 @@ to derive implementation test cases from interface test cases. See
`pydoc stocal.tests` for more information.


## Validation
Stocal ships with a validation suite for stochastic simulation
algorithms. The validation suite is based on the discrete stochastic
simulation model test suite DSMTS. To run validations, call
$ python stocal/examples/validation.py run N
from the command line. To generate a validation report, run
$ python stocal/examples/validation.py report
This generates a file validation.tex that can be compiled with pdflatex.
See
$ python stocal/examples/validation.py -h
for more information. The DSMTS user guide recommends N=1,000 as an
absolute minimum to be run regularly in conjunction with unit test,
and n=100,000 or n=1,000,000 for a thorough statistical analysis.
A rudimentary LaTeX template that collates report results into a
single document can be found in doc/validation.tex


## Releases

When preparing a new release, these steps should be followed

* git flow release start
* ensure an optimal code coverage of the test suite
* ensure that all tests pass
* ensure that any novel algorithm passes validation
* ensure that documentation (README, tutorial, etc.) is up to date
* update CHANGELOG.md
* bump the version number
Expand Down
84 changes: 66 additions & 18 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ process = Process([r1, r2])
```

We can use this process, to sample stochastic trajectories. The method
`Process.trajectory` instantiates a trajectory sampler for a given
`Process.sample` instantiates a trajectory sampler for a given
initial condition and stop criterion. The trajectory sampler implements
the iterator protocol, so we can simply iterate through the trajectory,
invoking one stochastic transition at a time. With each transition,
time and state of the trajectory are properly updated:

```python
trajectory = process.trajectory({'A':100}, steps=1000)
for transition in trajectory :
trajectory = process.sample({'A':100}, steps=1000)
for dt, transitions in trajectory :
print trajectory.time, trajectory.state['A'], trajectory.state['A2']
```

Expand Down Expand Up @@ -124,7 +124,7 @@ reactions. As such, rules generate a whole set of reactions.
Defining a rule requires to create a python
[class](https://docs.python.org/2/tutorial/classes.html) with some
required attributes and methods. The class needs to be derived from
`ReactionRule`, which requires our subclass to have the following
`TransitionRule`, which requires our subclass to have the following
attributes:

| attribute | description |
Expand All @@ -135,7 +135,7 @@ attributes:
Taking this all together, we define the following Dilution rule:

```python
class Dilution(ReactionRule) :
class Dilution(TransitionRule) :
Transition = MassAction

def novel_reactions(self, species) :
Expand All @@ -160,7 +160,7 @@ alternatively be provided as return type annotation of the
```python
from typing import Iterator

class Dilution(ReactionRule) :
class Dilution(TransitionRule) :
def novel_reactions(self, species) -> Iterator[MassAction]:
yield MassAction([species], [], 0.001)
```
Expand Down Expand Up @@ -191,7 +191,7 @@ To model this, we define a rule class for the polymerization that
generates a Polymerization reaction for any two reactants:

```python
class Polymerization(ReactionRule) :
class Polymerization(TransitionRule) :
Transition = MassAction

def novel_reactions(self, k, l) :
Expand All @@ -210,7 +210,7 @@ constants of these reactions depends on the lengths of the hydrolysis
products, so that polymers are more likely to break in the middle.

```python
class Hydrolysis(ReactionRule) :
class Hydrolysis(TransitionRule) :
Transition = MassAction

def novel_reactions(self, k) :
Expand All @@ -234,6 +234,19 @@ process = Process(transitions=[feed],
Note that no change is necessary for the dilution rule, since it already
generates a reaction for every chemical in the system.

*New in version 1.2:* Rule-based processes that expand into a finite
set of transitions can be flattened into equivalent static processes
that employ specific transitions rather than general rules:

```python
process = Process(rules=[Dilution()])
flat_process = process.flatten(['a', 'b', 'c'])
```
This will generate a new process objects where the original rule is
expanded into three transitions, each one modelling the specific
dilution of one of the provided molecular species.


## Complex States

So far, all our molecular species have been character sequences, either
Expand Down Expand Up @@ -337,7 +350,7 @@ potentially form two different polymerization products: _k+l_ and _l+k_.
Therefore, the polymerization rule has to generate both reactions:

```python
class Polymerization(ReactionRule) :
class Polymerization(TransitionRule) :
Transition = MassAction

def novel_reactions(self, k, l) :
Expand Down Expand Up @@ -383,7 +396,7 @@ with the above overloads for `__eq__`, `__ne__` and `__hash__`.
The nondirectional Polymerization rule now becomes:

```python
class Polymerization(ReactionRule) :
class Polymerization(TransitionRule) :
Transition = MassAction

def novel_reactions(self, k, l) :
Expand Down Expand Up @@ -412,14 +425,14 @@ reaction rules. For this example, we look into modelling the association
of proteins with mRNA's. We want to define a rule for the association of
an arbitrary protein with an arbitrary mRNA.

With the above ReactionRule's we would need to constantly check whether
the species supplied to `ReactionRule.novel_reactions` are indeed
With the above TransitionRule's we would need to constantly check whether
the species supplied to `TransitionRule.novel_reactions` are indeed
proteins and RNA's and only yield a transition in case they are. Not
knowing which argument of the reactant combination is the protein and
which the RNA further complicates the code.

```python
class Association(ReactionRule):
class Association(TransitionRule):
Transition = MassAction

def novel_reactions(self, k, l):
Expand All @@ -443,16 +456,16 @@ class Rna(str):
pass
```

We can now write a typed `ReactionRule` for their association, simply by
setting the optional ReactionRule attribute `signature` to the list of
types that the rule should accept. When defining a signature, it must
We can now write a typed `TransitionRule` for their association, simply
by setting the optional TransitionRule attribute `signature` to the list
of types that the rule should accept. When defining a signature, it must
have the same number of elements as the `novel_reactions` method.
`novel_reactions` will now only be called with arguments that adhere to
the type given in the signature. In our case, writing the rule becomes
as simple as:

```python
class Association(ReactionRule):
class Association(TransitionRule):
Transition = MassAction
signature = [Protein, Rna]

Expand All @@ -466,7 +479,7 @@ rule signature:
```python
from typing import Iterator

class Association(ReactionRule):
class Association(TransitionRule):
def novel_reactions(self, protein: Protein, rna: Rna) -> Iterator[MassAction]:
yield MassAction([protein, rna], [(protein,rna)], 1.)
```
Expand Down Expand Up @@ -514,6 +527,41 @@ have used default `MassAction` reactions before.
stocal/examples/temperature_cycle.py gives an example of how reactions
can be modified to take changing temperature instead of volumes instead.

## Stochastic simulation algorithms

stocal ships with several variants of the stochastic simulation algorithm,
refered to as sampler. A call to `Process.sample` inspects the
underlying process and will instantiate an appropriate sampler.
Currently, this creates an instance of Gibson and Bruck's next reaction
method, unless at least one transition of the process is time-dependent
(in which case the method creates an instance of Anderon's method).

If you want to control which simulation algorithm is instantiated, you
can instantiate the desired sampler directly, as in, e.g.,

```python
sampler = algorithms.DirectMethod(process, state, tmax=100.)
for dt, transitions in sampler:
print(dt, transitions)
```

Currently, stocal provides the following samplers:

| algorithm | description |
|------------------- | -------------------------------------------------------------------------------------------------------------- |
|DirectMethod | Original Gillespie algorithm |
|FirstReactionMethod | Stochastic simulation algorithm that can operate account for scheduled events |
|NextReactionMethod | Variant of FirstReactionMethod with improved performance *(new in version 1.2)* |
|AndersonMethod | Variant of NextReactionMethod that allows for propensity functions to be time-dependent *(new in version 1.1)* |
|CaoMethod | An (inexact) tau-leaping variant of SSA -- available in stocal.experimental.tauleap *(new in version 1.2)* |

Please refer to the class documentation for information about the exact
implementation and reference publication.

If you want to implement your own stochastic simulation algorithm, it
should be programmed against the interface defined by
`stocal.algorithms.StochasticSimulationAlgorithm`.

## Further Documentation

The full API of stocal is available via pydoc:
Expand Down
44 changes: 44 additions & 0 deletions doc/validation.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
\documentclass[notitlepage]{revtex4-1}
\usepackage{graphicx}
\graphicspath{{validation_data/1.1.2-64-g0d0fc6f/}}

\begin{document}
\title{DSMTS Validation results for \textit{stocal} samplers}
\author{version: \VAR{version}}
\maketitle{}

For each algorithm and model, figures show:
\begin{itemize}
\item on the left averaged trajectories of all system species over time
obtained by simulation (green) versus reported values from the DSMTS
repository (blue);

\item in the center the absolute standard error of sample averages
($\bar X_{t}$) from the reported mean ($\mu_{t}$) as a function of
sample size $N$:
\[
\left|\frac{\bar X_{t}-\mu_{t}}{\sigma_{t}^{2}}\right| \sim \mathcal{N}\left(0, \frac{1}{\sqrt{N}}\right),
\]
where $\sigma_{t}$ is the reported standard deviation.
The red line ($3/\sqrt{N}$) is an upper bound for the error scaling
at three times the reported standard deviation and should be only
occasionally exceeded;

\item on the right the relative standard error between sampled
($\bar S_{t}^{2}$) and reported standard deviation ($\sigma_{t}^{2}$)
as a function of sample size:
\[
\left|\frac{\bar S_{t}^{2}}{\sigma_{t}^{2}} - 1\right| \sim \mathcal{N}\left(0, \frac{2}{N}\right) .
\]
The red line ($5/\sqrt{N/2}$) is an upper bound for the error scaling
at five times the reported standard deviation and should be only
occasionally exceeded.
\end{itemize}

\BLOCK{ for method_name, figures in methods.items() }
\section{\VAR{method_name}}
\BLOCK{ for figure in figures }
\includegraphics[width=\textwidth]{\VAR{figure}}
\BLOCK{ endfor }
\BLOCK{ endfor }
\end{document}
7 changes: 4 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def readme():


setup(name = "stocal",
version = "1.1.2",
version = "1.2",
description = "simple rule-based stochastic simulation",
long_description = readme(),
classifiers=[
Expand All @@ -24,7 +24,8 @@ def readme():
author = "Harold Fellermann",
author_email = "[email protected]",
license='MIT',
packages = ["stocal", "stocal.examples", "stocal.tests"],
packages = ["stocal", "stocal.examples", "stocal.experimental", "stocal.tests"],
include_package_data=True,
zip_safe = True,
test_suite = 'stocal.tests')
test_suite = 'stocal.tests',
install_requires=['pqdict'])
3 changes: 2 additions & 1 deletion stocal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@

from .types import molecular_type
from .transitions import Transition, Reaction, MassAction, Event
from .transitions import Rule, ReactionRule, Process
from .transitions import Rule, ReactionRule, TransitionRule, Process
from .structures import multiset

from . import types
from . import algorithms
Expand Down
File renamed without changes.
Loading

0 comments on commit 4e464a0

Please sign in to comment.