├── .github └── workflows │ └── test-and-release.yml ├── .gitignore ├── CHANGELOG.md ├── DemoNotebooks ├── .gitignore ├── Agent_density.ipynb ├── AntHouseHunting.ipynb ├── COVID-19.ipynb ├── EpidemicsDemo_SIRI.ipynb ├── Michaelis-Menten_Dynamics.ipynb └── Variance_suppression.ipynb ├── LICENSE ├── README.md ├── TestNotebooks ├── MiscTests │ ├── MuMoTtest_1dstream.ipynb │ ├── MuMoTtest_3dstream.ipynb │ ├── MuMoTtest_GreekLetters.ipynb │ ├── MuMoTtest_MasterEq.ipynb │ ├── MuMoTtest_MultiController.ipynb │ ├── MuMoTtest_NoiseFixedPoints.ipynb │ ├── MuMoTtest_bifurcation.ipynb │ └── MuMoTtest_oneDimensionalModels.ipynb └── MuMoTtest.ipynb ├── apt.txt ├── docs ├── Makefile ├── MuMoTpaperResults.ipynb ├── MuMoTuserManual.ipynb ├── make.bat └── source │ ├── _static │ ├── .gitkeep │ ├── branch_model.png │ └── branch_model.svg │ ├── about.rst │ ├── api.rst │ ├── conf.py │ ├── development.rst │ ├── faq.rst │ ├── getting_started.rst │ ├── index.rst │ └── install.rst ├── mumot ├── __init__.py ├── consts.py ├── controllers.py ├── defaults.py ├── exceptions.py ├── models.py ├── utils.py └── views.py ├── postBuild ├── pyproject.toml ├── readthedocs.yml ├── requirements.txt ├── runtime.txt ├── setup.cfg ├── setup.py ├── tests └── test_all.py └── tox.ini /.github/workflows/test-and-release.yml: -------------------------------------------------------------------------------- 1 | name: test_and_upload_distribution 2 | on: 3 | # Trigger on tagged pushes to the master branch 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - 'v*' 9 | # Or on pull requests against the master branch 10 | pull_request: 11 | branches: 12 | - master 13 | jobs: 14 | test_and_upload_distr_job: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | # Spawn and run a job for each of three supported Python 3.x versions 18 | matrix: 19 | python: [3.6, 3.7, 3.8] 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | - name: Do non-shallow checkout to get all commits as needed if using setuptools_scm 24 | run: | 25 | git fetch --tags --prune --unshallow --force 26 | 27 | - name: Install MuMoT OS package dependencies 28 | run: sudo apt-get install -y texlive texlive-latex-extra graphviz dvipng 29 | - name: Set up a version of Python 30 | uses: actions/setup-python@v2 31 | with: 32 | python-version: ${{ matrix.python }} 33 | - name: Upgrade pip and install generic Python build and test tools 34 | run: | 35 | python -m pip install --upgrade pip 36 | python -m pip install tox codecov twine pep517 37 | 38 | - name: Run tests using tox 39 | run: tox -e py 40 | 41 | - name: Capture coverage data 42 | run: codecov 43 | if: matrix.python == '3.8' 44 | 45 | - name: Build a binary wheel and a source tarball 46 | run: python -m pep517.build --source --binary --out-dir dist/ . 47 | 48 | - name: Publish distributions to Test PyPI if this is a tagged commit 49 | if: success() && github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') 50 | env: 51 | TWINE_USERNAME: __token__ 52 | TWINE_PASSWORD: ${{ secrets.test_pypi_password }} 53 | run: | 54 | twine check dist/* 55 | twine upload --skip-existing --repository testpypi dist/* 56 | - name: Publish distributions to PyPI if this is a tagged commit 57 | if: success() && github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') 58 | env: 59 | TWINE_USERNAME: __token__ 60 | TWINE_PASSWORD: ${{ secrets.pypi_password }} 61 | run: twine upload --skip-existing dist/* 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # general things to ignore 2 | .DS_Store 3 | build/ 4 | dist/ 5 | *.egg-info/ 6 | *.egg 7 | *.py[cod] 8 | __pycache__/ 9 | *.so 10 | *~ 11 | .cache/ 12 | __mumot_files__/ 13 | 14 | # Test-related caches 15 | .tox/ 16 | .pytest_cache/ 17 | .coverage 18 | 19 | # Jupyter Notebooks checkpoints 20 | .ipynb_checkpoints/ 21 | 22 | # Autogenerated documentation 23 | docs/_build 24 | 25 | # PyCharm IDE files 26 | .idea/ 27 | .mypy_cache 28 | docs/source/autosummary 29 | .python-version 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## v1.2.2 4 | 5 | Enhancements: 6 | 7 | - Added more demo notebooks 8 | - Support for newer versions of certain Python dependencies (ipykernel, notebook, pyzmq and tornado) 9 | - `realtimePlot` (aka `runtimePlot`) is also available for `multiController`. 10 | However, this is available only when `shareAxes` is `False` or 11 | when the `realtimePlot` is the first view to be plotted. 12 | Other cases cannot be supported at the moment. 13 | - Added possibility to use the graphical keywords (`xlab`, `ylab`, `fontsize`, `legend_loc`, `legend_fontsize`, `choose_xrange`, `choose_yrange`) on all commands. 14 | - Added analysis to Variance suppression notebook 15 | 16 | ## v1.1.2 17 | 18 | Enhancements: 19 | 20 | - Various documentation improvements 21 | - Added another demo notebook 22 | - Show id of Figure objects in field views 23 | - Added possibility to have initial state >1 for `integrate()` and `bifurcation()` 24 | For `multiagent()` and `SSA()` the widgets are still limited to the sum of 1 25 | - Replace `latex2sympy`'s `process_sympy` with `sympy`'s `parse_latex` 26 | - Update docs to state Python >=3.6 required 27 | - Refactor MuMoT into separate modules 28 | this sets the length of time over which streams are integrated 29 | - `numPoints` added as keyword to 3D stream plot to set number of streams plotted 30 | - 3D stream plot now plots streams from random subset of starting points 31 | - 3D stream plot shading now based on velocity (calculated from line segment length) 32 | - 1D stream added 33 | - 3D stream added 34 | 35 | Bug fixes: 36 | 37 | - Guard against `iopub` rate limiting warnings 38 | - Increase `nbval` cell exec timeout 39 | - Suppress `matplotlib` deprecation warning in nested multicontrollers 40 | - Sum to 1 for all views; implement warnings correctly 41 | - Patched issue for 1D models 42 | - Patched issue with multiController 43 | - Fixed exceptions for stochastic analysis methods 44 | - Fixed widgets for rates with equation 45 | - Patched SSA bug 46 | 47 | ## v1.0.0 48 | 49 | - First release 50 | -------------------------------------------------------------------------------- /DemoNotebooks/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiODeProject/MuMoT/cb273bece99f5fddae8bc67780f41d1a29737f2c/DemoNotebooks/.gitignore -------------------------------------------------------------------------------- /DemoNotebooks/Agent_density.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# MuMoT Demonstration Notebook: Agent Density in Collective Decisions \n", 8 | "\n", 9 | "## Multiscale Modelling Tool \n", 10 | "\n", 11 | "*Yara Khaluf, Department of Information Technology, Ghent University;\n", 12 | "Andreagiovanni Reina, Department of Computer Science, University of Sheffield;\n", 13 | "Thomas Bose, Department of Computer Science, University of Sheffield;\n", 14 | "James A. R. Marshall, Department of Computer Science, University of Sheffield*\n", 15 | "\n", 16 | "# Introduction\n", 17 | "\n", 18 | "This notebook reproduces and extends the results of Khaluf *et al.* ([2017](#references)). In that paper, based on an earlier analysis by Biancalini *et al.* ([2014](#references)), the authors show that non-uniform distributions in swarms of agents have an impact on the scalability of collective decision-making. In particular, they highlight the relevance of noise-induced bistability in very sparse swarm systems and the failure of these systems to scale. Their work is based on three decision models: In the first model, each agent can change its decision after being recruited by a nearby agent. The second model captures the dynamics of dense swarms controlled by the majority rule (i.e., agents switch their opinion to comply with that of the majority of their neighbours). The third model combines the first two, with the aim of studying the role of non-uniform swarm density in the performance ofcollective decision-making. Based on the three models, the authors formulate a set of requirements for convergence and scalability in collective decision-making.\n", 19 | "\n" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": null, 25 | "metadata": { 26 | "scrolled": true 27 | }, 28 | "outputs": [], 29 | "source": [ 30 | "import mumot\n", 31 | "mumot.about()" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "## Defining the spontaneous-switching model\n", 39 | "The model presented in the next cell consists of two parts: (i) random recruitment with a reaction rate $r$, (ii) and spontaneous switch with rate $\\epsilon$." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "%%model\n", 49 | "$\n", 50 | "B -> A : \\epsilon\n", 51 | "A -> B : \\epsilon\n", 52 | "A + B -> A + A : r\n", 53 | "B + A -> B + B : r\n", 54 | "$" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "model1 = mumot.parseModel(In[-2])" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": null, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "model1.show()" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "model1.visualise()" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "# Results\n", 89 | "## Noise-free stability in infinite populations\n", 90 | "The model just presented only exhibits a single stable fixed point, regardless of parameterisation, as can be seen by analysis of the ODE system describing its infinite population, mean-field behaviour." 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "model1.showODEs()" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "Setting $dA/dt=dB/dt=0$ for non-zero $\\epsilon$ is only possible when $A$ = $B$, and this fixed point can be shown to be stable (Biancalini *et al.* [2014](#references))." 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "## Noise-induced bistability in a well-mixed system\n", 114 | "In contrast to its noise-free dynamics, the model displays noise-induced bistability. Depending on the rate coefficient $\\epsilon$, a critical system size $N_c=1/\\epsilon$ exists, such that the system is bistable for $N < N_c$ (*i.e.* bimodal stationary distributions) and unistable for $N > N_c$ (*i.e.* unimodal stationary distributions). \n", 115 | "\n", 116 | "You can experiment with the effect of varying $\\epsilon$ in the following simple multicontroller. The lefthand view is for system size $N=50$ whereas the righthand view is for $N=200$. Recall that the critical systemsize is $N_c=1/\\epsilon$, which for the initial value of $\\epsilon=0.01$ in the controller gives $N_c=100$." 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": { 123 | "scrolled": false 124 | }, 125 | "outputs": [], 126 | "source": [ 127 | "mc1 = mumot.MuMoTmultiController([model1.SSA(params = [('systemSize', 50)], silent = True),\n", 128 | " model1.SSA(params = [('systemSize', 200)], silent = True)],\n", 129 | " shareAxes = False, params = [('r', 1.0), ('plotLimits', 1)],\n", 130 | " initialState = {'A': 0.5, 'B': 0.5}, maxTime = 100.0, randomSeed = 112783855, \n", 131 | " plotProportions = True, initWidgets = {'epsilon':[0.01,0,0.1,0.005]},\n", 132 | " realtimePlot = False, aggregateResults = False, runs=1, visualisationType = 'evo')\n" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "The previous controller showed results from single simulation runs. We can view the distribution of system states after a fixed simulation duration with the following multicontroller. Again, the lefthand view is for system size $N=50$ whereas the righthand view is for $N=200$." 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": { 146 | "scrolled": false 147 | }, 148 | "outputs": [], 149 | "source": [ 150 | "mc2 = mumot.MuMoTmultiController([model1.SSA(params = [('systemSize', 20)], silent = True),\n", 151 | " model1.SSA(params = [('systemSize', 100)], silent = True)],\n", 152 | " shareAxes = False, params = [('r', 1.0), ('plotLimits', 1)],\n", 153 | " initialState = {'A': 0.5, 'B': 0.5}, maxTime = 10, randomSeed = 112783856, \n", 154 | " plotProportions = True, initWidgets = {'epsilon':[0.01,0,0.1,0.005]},\n", 155 | " realtimePlot = False, aggregateResults = False, runs=30,\n", 156 | " visualisationType = 'final', final_x = 'A', final_y = 'B')\n" 157 | ] 158 | }, 159 | { 160 | "cell_type": "markdown", 161 | "metadata": {}, 162 | "source": [ 163 | "## Effects of space on system dynamics\n", 164 | "\n", 165 | "The next cell shows the impact of the interaction network on the system dynamics. With $\\epsilon=0.01$ and a system size of S=40 reactants, the system is parameterised under the critical threshold predicting symmetry breaking (S<100).\n", 166 | "\n", 167 | "By changing the interaction range, we can obtain symmetry breaking or deadlock. If the interaction range is small (try 0.05), the noise dominates the dynamics (equivalent to large $\\epsilon$), conversely when the interaction range is large (try 0.5) the influence of noise is counteracted by recruitment (rate $r$) and the dynamics are similar to the mean-field analyses illustrated above. Similarly, the same effect can be observed with other network types (*e.g.* try a Barabasi-Albert network and vary the connectivity parameter `new edges`)." 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "ma1 = model1.multiagent(initWidgets = {'\\epsilon':[0.01,0,0.1,0.005], 'maxTime':[100,5,200,5], 'netType':'dynamic',\n", 177 | " 'systemSize':[40,10,200,1], 'showInteractions':True},\n", 178 | " initialState = {'A': 0.5, 'B': 0.5}, params = [('r', 1.0)], randomSeed=261594811,\n", 179 | " timestepSize=0.99, motionCorrelatedness = 0.6, particleSpeed = 0.01, plotProportions = True)" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "## Relationship to models of honeybee house-hunting\n", 187 | "### Approximation for indirect switching\n", 188 | "The above model (Biancalini *et al.* [2014](#references), Khaluf *et al.*, [2017](#references)) is similar to the *Apis mellifera* model *House-hunting with indirect switching* of Marshall *et al.* ([2009](#references)). The latter model is defined below for the symmetric, equal alternatives, case and has the additional state $U$ representing the uncommitted bees. The model has three main transitions: discovery from $U$ to $A$ (or $B$) at rate $q$; decay from $A$ (or $B$) to $U$ at rate $k$; and recruitment of $U$ at rate $g$.\n", 189 | "\n", 190 | "In this case, we can approximate the spontaneous switch $\\epsilon$ (from Khaluf *et al.* ([2017](#references))) as the combination of the transitions of decay $A \\xrightarrow{k} U$ and discovery $U \\xrightarrow{q} B$ (or $A$), i.e. \n", 191 | "$A \\xrightarrow{k} U \\xrightarrow{\\frac{q}{2}} B$\n", 192 | "Therefore, we approximate $\\epsilon \\approx \\frac{k\\,q}{2}$.\n", 193 | "Similarly, the recuritment $r$ from Khaluf *et al.* ([2017](#references)) can be approximated as $r \\approx k\\,g$." 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "metadata": {}, 200 | "outputs": [], 201 | "source": [ 202 | "%%model\n", 203 | "$\n", 204 | "U -> A : q\n", 205 | "U -> B : q\n", 206 | "A -> U : k\n", 207 | "B -> U : k\n", 208 | "U + A -> A + A : g\n", 209 | "U + B -> B + B : g\n", 210 | "$" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": null, 216 | "metadata": {}, 217 | "outputs": [], 218 | "source": [ 219 | "model2 = mumot.parseModel(In[-2]).substitute('U=N-A-B')" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "metadata": {}, 226 | "outputs": [], 227 | "source": [ 228 | "model2.show()" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": null, 234 | "metadata": {}, 235 | "outputs": [], 236 | "source": [ 237 | "model2.visualise()" 238 | ] 239 | }, 240 | { 241 | "cell_type": "markdown", 242 | "metadata": {}, 243 | "source": [ 244 | "For this model we can first explore our intuition that there is only one stable attractor in the infinite population case." 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": { 251 | "scrolled": false 252 | }, 253 | "outputs": [], 254 | "source": [ 255 | "str1 = model2.stream('A','B', showFixedPoints = True)" 256 | ] 257 | }, 258 | { 259 | "cell_type": "markdown", 260 | "metadata": {}, 261 | "source": [ 262 | "Next, using the above approximation, we set the following parameters $k=0.01$, $g=100$, and $q=1$. This leads to \n", 263 | "$r \\approx k\\,g=1$ and $\\epsilon \\approx \\frac{k\\,q}{2} = 0.005$. The critical system size is thus $N_c \\approx 200$. In the following multicontroller the lefthand view is for system size $N=50$, and the righthand view is for $N=300$." 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | "mc3 = mumot.MuMoTmultiController([model2.SSA(params = [('systemSize', 50)], silent = True),\n", 273 | " model2.SSA(params = [('systemSize', 300)], silent = True)],\n", 274 | " shareAxes = False, params = [('g', 100.0), ('plotLimits', 1)],\n", 275 | " initialState = {'A': 0.1, 'B': 0.1, 'U': 0.8}, maxTime = 2000.0, randomSeed = 112783855, \n", 276 | " plotProportions = True, initWidgets = {'k':[0.01,0,0.1,0.005],'q':[1,0,1,0.05]},\n", 277 | " realtimePlot = False, aggregateResults = False, runs=1, visualisationType = 'evo')\n" 278 | ] 279 | }, 280 | { 281 | "cell_type": "markdown", 282 | "metadata": {}, 283 | "source": [ 284 | "### Equivalence for direct switching with spontaneous switches\n", 285 | "\n", 286 | "The preceding analysis was an approximation to a model of indirect switching. However, Marshall *et al.* ([2009](#references)) also analysed a *direct switching* model and argued that this decision-making model is statistically optiml for decay $k=0$. Yet decision-making in this model is pathological in the case where two equal alternatives are under consideration, and decay is zero; here a means of breaking symmetry is required (Seeley *et al.*, [2012](#references)). In fact, when $k=0$ then the uncommitted population $U$ will approach 0, and in this limit the symmetric honeybee house-hunting model is identical to the model considered by Khaluf *et al.* ([2017](#references)) but with $\\epsilon=0$. Thus noise-induced bistability should be able to break deadlock in the model of Marshall *et al.* ([2009](#references)) by adding additional transitions, as can be seen below." 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "metadata": {}, 293 | "outputs": [], 294 | "source": [ 295 | "%%model\n", 296 | "$\n", 297 | "U -> A : q\n", 298 | "U -> B : q\n", 299 | "A -> B : \\epsilon\n", 300 | "B -> A : \\epsilon\n", 301 | "U + A -> A + A : g\n", 302 | "U + B -> B + B : g\n", 303 | "A + B -> A + A : r\n", 304 | "A + B -> B + B : r\n", 305 | "$" 306 | ] 307 | }, 308 | { 309 | "cell_type": "markdown", 310 | "metadata": {}, 311 | "source": [ 312 | "Again, we explore the infinite population model, seeing that there is only ever a single stable attractor." 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [ 321 | "model3 = mumot.parseModel(In[-2]).substitute('U=N-A-B')\n", 322 | "model3.show()" 323 | ] 324 | }, 325 | { 326 | "cell_type": "code", 327 | "execution_count": null, 328 | "metadata": {}, 329 | "outputs": [], 330 | "source": [ 331 | "model3.visualise()" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "metadata": {}, 338 | "outputs": [], 339 | "source": [ 340 | "str2 = model3.stream('A','B', showFixedPoints = True, params = [('r', 1.0)])" 341 | ] 342 | }, 343 | { 344 | "cell_type": "markdown", 345 | "metadata": {}, 346 | "source": [ 347 | "Next, we can make use of the exact same calculation of the critical system size $N_c=1/\\epsilon$ used above to determine when noise-induced bistability should be observed." 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": null, 353 | "metadata": {}, 354 | "outputs": [], 355 | "source": [ 356 | "mc4 = mumot.MuMoTmultiController([model3.SSA(params = [('systemSize', 50)], silent = True),\n", 357 | " model3.SSA(params = [('systemSize', 200)], silent = True)],\n", 358 | " shareAxes = False, params = [('g', 1.0), ('q', 1.0), ('r', 1.0), ('plotLimits', 1)],\n", 359 | " initialState = {'A': 0.1, 'B': 0.1, 'U': 0.8}, initWidgets = {'epsilon':[0.01,0,0.1,0.005]},\n", 360 | " maxTime = 100.0, randomSeed = 112783855, plotProportions = True, realtimePlot = False,\n", 361 | " aggregateResults = False, runs=1, visualisationType = 'evo')\n", 362 | "\n" 363 | ] 364 | }, 365 | { 366 | "cell_type": "markdown", 367 | "metadata": {}, 368 | "source": [ 369 | "# Discussion\n", 370 | "\n", 371 | "The results presented above have shown how noise-induced bistability can break decision deadlocks. However, introducing spontaneous switching is not a silver bullet. While spontaneous switching can break deadlock in the case of equal decison alternatives, it can also make decision-making suboptimal when alternatives differ. Optimality in the direct-switching model analysed by Marshall *et al.* can be shown by reducing the dynamics of the model when $U=0$ to the statistically-optimal Drift-Diffusion Model (DDM) of decision-making (Marshall *et al.* [2009](#references))\n", 372 | "$$\n", 373 | "\\dot{x}=A+\\sigma \\eta\n", 374 | "$$\n", 375 | "where $\\dot{x}$ is the change in evidence variable $x$, $A$ is constant drift proportional to decision signal, and $\\sigma \\eta$ is Gaussian noise proportional to decision noise.\n", 376 | "\n", 377 | "Introducing spontaneous switches it is easy to show, using the method presented in Marshall *et al.* ([2009](#references)), that the decision process when $U=0$ becomes\n", 378 | "$$\n", 379 | "\\dot{x}=A+ B(x + \\sigma' \\eta) + \\sigma \\eta\n", 380 | "$$\n", 381 | "where $B<0$ is a stabilising term an $\\sigma'$ is a second noise process, making the decision process akin to a generalised stable Ornstein-Uhlenbeck process, which must be suboptimal." 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": null, 387 | "metadata": {}, 388 | "outputs": [], 389 | "source": [ 390 | "%%model\n", 391 | "$\n", 392 | "U -> A : q_A\n", 393 | "U -> B : q_B\n", 394 | "A -> B : \\epsilon\n", 395 | "B -> A : \\epsilon\n", 396 | "U + A -> A + A : q_A\n", 397 | "U + B -> B + B : q_B\n", 398 | "A + B -> A + A : q_A\n", 399 | "A + B -> B + B : q_B\n", 400 | "$" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": null, 406 | "metadata": {}, 407 | "outputs": [], 408 | "source": [ 409 | "model4 = mumot.parseModel(In[-2]).substitute('U=N-A-B')\n", 410 | "\n", 411 | "str3 = model4.stream('A','B', showFixedPoints = True)" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "metadata": {}, 417 | "source": [ 418 | "In contrast, the *stop-signalling* model of house-hunting in honeybees (Seeley *et al.*, [2012](#references)) is able both to break decision deadlock in the case of equal decision alternatives, and approximate the statistically optimal DDM when alternatives differ (Pais *et al.*, [2013](#references))." 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": {}, 424 | "source": [ 425 | "# References \n", 426 | "\n", 427 | "\n", 428 | "* Biancalani, T., Dyson, L., McKane, A.J., (2014) [Noise-induced bistable states and their mean switching time in foraging colonies](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.112.038101). *Physical Review Letters* **112**(3), 038101.\n", 429 | "* Khaluf, Y., Pinciroli, C., Valentini, G., Hamann, H. (2017) [The impact of agent density on scalability in collective systems: noise-induced versus majority-based bistability](https://doi.org/10.1007/s11721-017-0137-6). *Swarm Intelligence* **11**: 155-179. \n", 430 | "* Marshall, J. A. R., Bogacz, R., Dornhaus, A. Planqué, R., Kovacs, T. & Franks, N. R. (2009) [On optimal decision making in brains and social insect colonies](https://doi.org/10.1098/rsif.2008.0511). *Journal of the Royal Society: Interface* **6**, 1065-1074.\n", 431 | "* Pais, D., Hogan, P.M., Schlegel, T., Franks, N.R., Leonard, N.E. & Marshall, J.A.R. (2013) [A mechanism for value-sensitive decision-making](http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0073216). *PLoS one* **8**(9), e73216\n", 432 | "* Seeley, T.D, Visscher, P.K. Schlegel, T., Hogan, P.M., Franks, N.R. & Marshall, J.A.R. (2012) [Stop signals provide cross inhibition in collective decision-making by honeybee swarms](http://www.sciencemag.org/content/335/6064/108.full.pdf). *Science* **335**, 108-111" 433 | ] 434 | } 435 | ], 436 | "metadata": { 437 | "kernelspec": { 438 | "display_name": "Python 3", 439 | "language": "python", 440 | "name": "python3" 441 | }, 442 | "language_info": { 443 | "codemirror_mode": { 444 | "name": "ipython", 445 | "version": 3 446 | }, 447 | "file_extension": ".py", 448 | "mimetype": "text/x-python", 449 | "name": "python", 450 | "nbconvert_exporter": "python", 451 | "pygments_lexer": "ipython3", 452 | "version": "3.7.4" 453 | }, 454 | "toc": { 455 | "base_numbering": 1, 456 | "nav_menu": {}, 457 | "number_sections": true, 458 | "sideBar": true, 459 | "skip_h1_title": false, 460 | "title_cell": "Table of Contents", 461 | "title_sidebar": "Contents", 462 | "toc_cell": false, 463 | "toc_position": {}, 464 | "toc_section_display": true, 465 | "toc_window_display": true 466 | } 467 | }, 468 | "nbformat": 4, 469 | "nbformat_minor": 2 470 | } 471 | -------------------------------------------------------------------------------- /DemoNotebooks/AntHouseHunting.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Ant recruitment model" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import mumot" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "%%model\n", 26 | "U -> A : d_A\n", 27 | "U -> B : d_B\n", 28 | "A -> U : a_A\n", 29 | "B -> U : a_B\n", 30 | "A + U -> A + A : r_A\n", 31 | "B + U -> B + B : r_B\n", 32 | "A + B -> A + A : s_A\n", 33 | "A + B -> B + B : s_B" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "model1 = mumot.parseModel(In[2])" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "model2 = model1.substitute('U = N - A - B')" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "model3 = model2.substitute('d_A = d * v_A, d_B = d * v_B, a_A = a / v_A, a_B = a / v_B, r_A = r * v_A, r_B = r * v_B, s_A = s * v_A, s_B = s * v_B')" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "model4 = model3.substitute('v_A = (V+\\\\Delta)/2, v_B = (V-\\\\Delta)/2')" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "model4.show()" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "model5 = model4.substitute(\"a = 1, r = 1, d = 1\")" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "model5.showODEs()" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "stream1 = model5.stream('A','B')" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "model5.SSA()" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "bifurcation1 = model5.bifurcation('s', 'A', initWidgets={'Delta':[1, 0, 5, 0.1]})" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [] 132 | } 133 | ], 134 | "metadata": { 135 | "kernelspec": { 136 | "display_name": "Python 3", 137 | "language": "python", 138 | "name": "python3" 139 | }, 140 | "language_info": { 141 | "codemirror_mode": { 142 | "name": "ipython", 143 | "version": 3 144 | }, 145 | "file_extension": ".py", 146 | "mimetype": "text/x-python", 147 | "name": "python", 148 | "nbconvert_exporter": "python", 149 | "pygments_lexer": "ipython3", 150 | "version": "3.5.4" 151 | }, 152 | "toc": { 153 | "base_numbering": 1, 154 | "nav_menu": {}, 155 | "number_sections": true, 156 | "sideBar": true, 157 | "skip_h1_title": false, 158 | "title_cell": "Table of Contents", 159 | "title_sidebar": "Contents", 160 | "toc_cell": false, 161 | "toc_position": {}, 162 | "toc_section_display": true, 163 | "toc_window_display": false 164 | } 165 | }, 166 | "nbformat": 4, 167 | "nbformat_minor": 2 168 | } 169 | -------------------------------------------------------------------------------- /DemoNotebooks/COVID-19.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# MuMoT Demonstration Notebook: SEIR-Derived Epidemiological Models of COVID-19 and Social Distancing\n", 8 | "\n", 9 | "## Multiscale Modelling Tool \n", 10 | "\n", 11 | "*James A. R. Marshall, Department of Computer Science, University of Sheffield*\n", 12 | "\n", 13 | "# Introduction\n", 14 | " \n", 15 | "This notebook aims to help explain the possible epidemiology of and effects of different public interventions on the COVID-19 novel coronavirus infectious disease, caused by the SARS-CoV-2 virus (WHO, 2020). Models are presented to show how different health policies may impact on the progression of the disease through a population. The models are simple 'toy' models for illustrative purposes, although model parameters are taken from the literature on the disease.\n", 16 | "\n", 17 | "_**If you are a non-specialist, you may simply want to go to the `Kernel` menu above, select `Restart & Run All`, then skip to the interactive plots and use these, along with reading their accompanying bold text.**_" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "import mumot" 27 | ] 28 | }, 29 | { 30 | "cell_type": "markdown", 31 | "metadata": {}, 32 | "source": [ 33 | "# Model Definitions\n", 34 | "\n", 35 | "Our model is adapted from the standard SEIR model (Susceptiple, Exposed, Infectious, Recovered). The SEIR model is a model used to represent infectious diseases with a latent period following infection, before an individual becomes infectious, and has been used in modelling COVID-19 (e.g.Wu et al., 2020). However, since COVID-19 may exhibit significant presymptomatic infectiousness (Anderson et al., 2020; Du et al.2020, Tindal et al., 2020), we also include an infection process from members of the $E$ class, which is not usual in SEIR models; comparatively few models of COVID-19 to date have explicitly included infection by asymptomatic individuals (see Cao et al. (2020) and Li et al.(2020) for exceptions). Since asymptomatic individuals apparently can also recover directly without becoming symptomatic, we also adapt the SEIR model to allow direct recovery from the exposed class $E$. For simplicity, we assume the same infection rate $r$ regardless of whether a person is in $E$ or $I$.\n", 36 | "\n", 37 | "For COVID-19 infection rates are assumed to be higher than recovery rates.\n", 38 | "\n", 39 | "For health systems, the key population to track is the infectious population $I$, which in our model represents the symptomatic members of the population, since these are the ones that will require healthcare resources such as medicines, medical callouts, or hospitalisation. _Note that this model does not explicitly represent deaths from infection - all population members eventually recover, which is clearly not the case for COVID-19_\n", 40 | "\n", 41 | "Thus our model is defined as follows:" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "%%model\n", 51 | "$\n", 52 | "S + I -> I + E: r * s\n", 53 | "S + E -> E + E: r * (1 - s)\n", 54 | "E -> I: a\n", 55 | "E -> R: g\n", 56 | "I -> R: g\n", 57 | "$" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "fullmodel = mumot.parseModel(In[-2])\n", 67 | "finitemodel = fullmodel.substitute('S = N - I - E - R')\n", 68 | "finitemodel.show()" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "# Results\n", 76 | "In the following, we analyse the models using both infinite-population ODEs, and finite population spatial stochastic simulations. _The latter are particularly good for allowing us to vary *social distancing* and see the effects on the infection's progress_.\n", 77 | "\n", 78 | "## Social Distancing Across the Population\n", 79 | "We parameterise the model to approximate the characteristics of COVID-19; the purpose of this demonstration is to show the effects of social distancing, so exact parameterisation is not required but could be found from the literature (see sample literature in References). Here we choose $r=2.5$ and $a=g=1/2$; estimates of infectiousness vary but are higher than previous comparable diseases (see Liu et al., 2020, for a review) estimates of asymptomatic transmissions share of total infections vary between being a minority (Du et al., 2020) and, potentially, a majority (Tindal et al., 2020; Li et al., 2020); we assume infectiousness for members of $E$ is half that of members in $I$ ($s=2/3$) (Li et al., 2020). We then present a controller in which there is a single slider to change the degree of social isolation. This enables us to easily compare the effects of no social isolation (a 'well-mixed' population, left) with social isolation (a spatial, partially connected population, right)." 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "_**You can see how social distancing affects the spread of the disease within the population by looking inside the population as individuals mingle and interact. Open the 'Advanced options' tab and vary the interaction range between 0 (full social distancing) and 1 (no social distancing), then observe the effect on the population. Recall that green individuals are the ones who may need hospital treatment, and the red individuals are recovered. Implementing social distancing means the virus has fewer opportunities to jump between individuals, so doesn't spread as fast.**_" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "agentcont1 = finitemodel.multiagent(ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, initWidgets={'netParam':[1.0, 0, 1.0, 0.05]}, maxTime = 10.0, timestepSize = 0.25, randomSeed = 731529356, netType = 'dynamic', motionCorrelatedness = 0.5, particleSpeed = 0.01, showTrace = False, showInteractions = False, visualisationType = 'graph', plotProportions = True, realtimePlot = True, runs = 1, aggregateResults = True, bookmark = False, silent = False, params = [('s', 2/3),('a', 0.5), ('g', 0.5), ('r', 2.0), ('plotLimits', 1), ('systemSize', 100.0)])" 96 | ] 97 | }, 98 | { 99 | "cell_type": "markdown", 100 | "metadata": {}, 101 | "source": [ 102 | "Open the 'Advanced options' tab again and vary the interaction range between 0 (full social distancing) and 1 (no social distancing), then observe the effect on a sample infection trajectory in the right hand plot, compared to the left hand reference plot (no social distancing). The plot shows the fractions of the total population that are in the different disease states, indicated by the figure lengends. Note that social distancing not only reduces the peak number of cases needing medical attention, but also the total number of infections.\n", 103 | "\n", 104 | "_**As you vary the interaction range slider between full social contact (1.0) and no social contact (0.0) you should observe a threshold effect at around 0.10, where the progress of the disease is drastically reduced; the maximum proportion of cases at any point in time (green line) reduces.**_" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "metadata": { 111 | "scrolled": false 112 | }, 113 | "outputs": [], 114 | "source": [ 115 | "multicont1 = mumot.MuMoTmultiController(\n", 116 | " [finitemodel.integrate(showStateVars=['E', 'I', 'R'], ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, maxTime = 10.0, plotProportions = True, silent = True, conserved = True, bookmark = False),\n", 117 | " finitemodel.multiagent(ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, initWidgets={'netParam':[1.0, 0, 1.0, 0.05]}, maxTime = 10.0, timestepSize = 0.25, randomSeed = 731529356, netType = 'dynamic', motionCorrelatedness = 0.5, particleSpeed = 0.01, showTrace = False, showInteractions = False, visualisationType = 'evo', plotProportions = True, realtimePlot = False, runs = 1, aggregateResults = True, silent = True, bookmark = False)], params = [('s', 2/3),('a', 0.5), ('g', 0.5), ('r', 2.5), ('plotLimits', 1), ('systemSize', 100.0)],\n", 118 | " choose_yrange = [0, 1])" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": {}, 124 | "source": [ 125 | "## Social Distancing vs Isolating Symptomatic Cases\n", 126 | "\n", 127 | "We now adapt our model to examine the possible relative effectiveness of social distancing, compared to isolating only symptomatic individuals. The basic infectiousness of the disease $r$ can be varied, as can the share of infections generated by symptomatic individuals $I$ ($s$). We make a new model in which symptomatic individuals are isolated so reduce their infectivity ten-fold, and introduce a social distancing parameter $d$ that scales contacts between asymptomatic individuals ($E$) and susceptible individuals ($S$). For this scenario we now assume asymptomatic infectiousness is twice that of symptomatic (cf. Tindal et al., 2020; Li et al., 2020), $s=1/3$." 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "%%model\n", 137 | "$\n", 138 | "S + I -> I + E: r * s / 10\n", 139 | "S + E -> E + E: r * d * (1 - s)\n", 140 | "E -> I: a\n", 141 | "E -> R: g\n", 142 | "I -> R: g\n", 143 | "$" 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "_**Vary the social distancing parameter $d$ to see the difference that social distancing by asymptotic carriers makes (righthand plot), on top of isolation only of symptomatic cases (lefthand plot). You can also vary the infectiousness of the disease ($r$) and proportion of infections due to symptomatic carriers ($s$), since there is some uncertainty around these.**_" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": null, 156 | "metadata": {}, 157 | "outputs": [], 158 | "source": [ 159 | "compmodel1 = mumot.parseModel(In[-2]).substitute('S = N - I - E - R')\n", 160 | "compmodel1.show()" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "multicont2 = mumot.MuMoTmultiController(\n", 170 | " [compmodel1.integrate(showStateVars=['E', 'I', 'R'], ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, maxTime = 10.0, choose_yrange = [0, 1], plotProportions = True, silent = True, conserved = True, bookmark = False, params = [('d', 1)]),\n", 171 | " compmodel1.integrate(showStateVars=['E', 'I', 'R'], ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, maxTime = 10.0, choose_yrange = [0, 1], plotProportions = True, silent = True, conserved = True, bookmark = False)],\n", 172 | " initWidgets={'r':[2.5, 0, 4, 0.1],'s':[0.35, 0, 1, 0.05], 'd':[1, 0.5, 1, 0.05]}, params = [('a', 0.5), ('g', 0.5), ('plotLimits', 1), ('systemSize', 10)])" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "# Whole-Population Social Distancing vs Distancing for High Risk Groups\n", 180 | "\n", 181 | "Similarly to examining the effects of isolation only on symptomatic individuals above, Benjamin Kerr (2020) and Omar Cornejo (2020) constructed a model of social distancing for different risk groups. Here we adapt our modified SEIR model to include their concept of high and low risk groups. The basic rate parameters for the disease are the same for each, but social distancing factors can be applied to each independently." 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": null, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "%%model\n", 191 | "$\n", 192 | "S_h + E_h -> E_h + E_h: r * (1 - s) * d_h\n", 193 | "S_l + E_h -> E_l + E_h: r * (1 - s) * d_l\n", 194 | "S_h + E_l -> E_h + E_l: r * (1 - s) * d_h\n", 195 | "S_l + E_l -> E_l + E_l: r * (1 - s) * d_l\n", 196 | "S_h + I_h -> E_h + I_h: r * s * d_h\n", 197 | "S_l + I_h -> E_l + I_h: r * s * d_l\n", 198 | "S_h + I_l -> E_h + I_l: r * s * d_h\n", 199 | "S_l + I_l -> E_l + I_l: r * s * d_l\n", 200 | "E_l -> I_l: a\n", 201 | "E_h -> I_h: a\n", 202 | "E_l -> R: g\n", 203 | "E_h -> R: g\n", 204 | "I_l -> R: g\n", 205 | "I_h -> R: g\n", 206 | "$" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": null, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "compmodel2 = mumot.parseModel(In[-2]).substitute('S = N - I_h - I_l - E_h - E_l - R')\n", 216 | "compmodel2.show()" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "The model is paramaterised as follows: The susceptible population is 85:15 low risk:high risk, and initial carriers are equally divided between the low risk and high risk groups. The initial 5% of infected individuals are half low risk, half high risk. As above, infection rates of symptomatic ($r$) and asymptomatic ($r * s$) can be manipulated." 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "metadata": {}, 229 | "source": [ 230 | "_**In the following plot you can manipulate the extent of social distancing for the high-risk ($d_h$) and low-risk ($d_l$) groups, from 1 (no social distancing for that group) to 0 (full social isolation for that group). The plots show (top-left) disease progression with no social distancing measures on any group; (top-right) progression with social distancing only on the high-risk group (bottom-left) progression with social distancing only on the low-risk group; (bottom-right) progression with social distancing only on the high-risk group. Low risk (dashed cyan) and high risk (solid red) infected populations are shown**_" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [ 239 | "multicont2 = mumot.MuMoTmultiController(\n", 240 | " [compmodel2.integrate(showStateVars=['I_h', 'I_l'], ylab = 'cases', initialState = {'E_h': 0.025, 'I_h': 0.0, 'E_l': 0.025, 'I_l': 0.0, 'R': 0.0, 'S_h': 0.15, 'S_l': 0.8}, maxTime = 10.0, choose_yrange = [0, 0.3], plotProportions = True, silent = True, conserved = True, bookmark = False, params = [('d_h', 1), ('d_l', 1)]),\n", 241 | " compmodel2.integrate(showStateVars=['I_h', 'I_l'], ylab = 'cases', initialState = {'E_h': 0.025, 'I_h': 0.0, 'E_l': 0.025, 'I_l': 0.0, 'R': 0.0, 'S_h': 0.15, 'S_l': 0.8}, maxTime = 10.0, choose_yrange = [0, 0.3], plotProportions = True, silent = True, conserved = True, bookmark = False, params = [('d_l', 1)]),\n", 242 | " compmodel2.integrate(showStateVars=['I_h', 'I_l'], ylab = 'cases', initialState = {'E_h': 0.025, 'I_h': 0.0, 'E_l': 0.025, 'I_l': 0.0, 'R': 0.0, 'S_h': 0.15, 'S_l': 0.8}, maxTime = 10.0, choose_yrange = [0, 0.3], plotProportions = True, silent = True, conserved = True, bookmark = False, params = [('d_h', 1)]),\n", 243 | " compmodel2.integrate(showStateVars=['I_h', 'I_l'], ylab = 'cases', initialState = {'E_h': 0.025, 'I_h': 0.0, 'E_l': 0.025, 'I_l': 0.0, 'R': 0.0, 'S_h': 0.15, 'S_l': 0.8}, maxTime = 10.0, choose_yrange = [0, 0.3], plotProportions = True, silent = True, conserved = True, bookmark = False)],\n", 244 | " initWidgets={'r':[2.5, 0, 4, 0.1],'s':[0.35, 0, 1, 0.05], 'd_{h}':[0.25, 0, 1, 0.05], 'd_{l}':[0.25, 0, 1, 0.05]}, params = [('a', 0.5), ('g', 0.5), ('plotLimits', 1), ('systemSize', 10)])" 245 | ] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": {}, 250 | "source": [ 251 | "# Discussion\n", 252 | "_**The purpose of this notebook has been to highlight, using simple models that still capture key aspects of the disease, how various public health policy interventions might affect\n", 253 | "the spread of COVID-19. The key messages are:**_\n", 254 | "\n" 259 | ] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "metadata": {}, 264 | "source": [ 265 | "# References \n", 266 | "\n", 267 | "" 279 | ] 280 | } 281 | ], 282 | "metadata": { 283 | "kernelspec": { 284 | "display_name": "Python 3", 285 | "language": "python", 286 | "name": "python3" 287 | }, 288 | "language_info": { 289 | "codemirror_mode": { 290 | "name": "ipython", 291 | "version": 3 292 | }, 293 | "file_extension": ".py", 294 | "mimetype": "text/x-python", 295 | "name": "python", 296 | "nbconvert_exporter": "python", 297 | "pygments_lexer": "ipython3", 298 | "version": "3.7.6" 299 | }, 300 | "toc": { 301 | "base_numbering": 1, 302 | "nav_menu": {}, 303 | "number_sections": true, 304 | "sideBar": true, 305 | "skip_h1_title": false, 306 | "title_cell": "Table of Contents", 307 | "title_sidebar": "Contents", 308 | "toc_cell": false, 309 | "toc_position": {}, 310 | "toc_section_display": true, 311 | "toc_window_display": true 312 | } 313 | }, 314 | "nbformat": 4, 315 | "nbformat_minor": 2 316 | } 317 | -------------------------------------------------------------------------------- /DemoNotebooks/Michaelis-Menten_Dynamics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# MuMoT Demonstration Notebook: Michaelis Menten \n", 8 | " \n", 9 | "*Aldo Estefano Encarnacion Segura, Department of Computer Science, University of Sheffield*\n", 10 | "\n", 11 | "\n", 12 | "This is a demo for [MuMoT](https://github.com/DiODeProject/MuMoT), a software tool developed at the University of Sheffield as part of the [DiODe](http://diode.group.shef.ac.uk) project" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "import mumot\n", 22 | "\n", 23 | "mumot.setVerboseExceptions()" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "### Introduction to the Michaelis-Menten model\n", 31 | "\n", 32 | "In this short demo we will analyse one of the simplest models of enzyme kinetics, the Michaelis-Menten model. We will explore the four-dimensional original model, and then we will focus on two modified equations from the model after reducing the dynamic description of the model.\n", 33 | "\n", 34 | "The original paper was published in German in 1913 ([Michaelis & Menten, 1913](#references)). An English translation of the original article as well as a historical context and summary was published by Johnson & Goody ([2011](#references)). For a general overview of the model see Murray ([2002](#references), p.175).\n", 35 | "\n", 36 | "The Michaelis-Menten dynamics describe product formation as a result of an enzymatic reaction. Substrate $S$ binds irreversibly with enzyme $E$ to form a complex $C$, which in turn delivers the product $P$ and the enzyme.\n" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "#### Michaelis-Menten original model" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "model = mumot.parseModel(r\"\"\"\n", 53 | "E + S -> C + \\emptyset : k_f\n", 54 | "C + \\emptyset -> E + S : k_r\n", 55 | "C + \\emptyset -> E + P : k_c\n", 56 | "\"\"\")" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "model.show()" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "model.showODEs()" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "We have declared the reactions of the model and MuMoT has derived the ordinary differential equations that correspond to the four components of our system. Each term is a different reaction that either increases or decreases a variable's concentration. \n", 82 | "\n", 83 | "The reaction scheme is usually portrayed as: $S + E ⟷ C \\rightarrow P + E$ " 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "model.visualise()" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "We can integrate our model to track how the concentration of each of our components change over time. The parameters used are arbitrarily chosen for demonstration purposes. We start with substrate $S$ and $E$ present and no $P$ nor the intermediary complex $C$, which are yet to be formed.\n", 100 | "\n", 101 | "The integration shows that both the enzyme and the complex go through a transient phase in which their concentration dips or rises before returning to its initial state. On the other hand, the substrate has been irreversibly transformed into the product. " 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "int0 = model.integrate(initWidgets = {'\\k_c':[0.1, 0, 0.5, 0.1],\n", 111 | " '\\k_f':[1, 0, 10, 0.1],\n", 112 | " '\\k_r':[0.01, 0, 0.1, 0.01],\n", 113 | " 'initialState':{'S': [0.5,0,1,0.1],'E': [0.5,0,1,0.1],'P': [0,0,1,0.1],'C': [0,0,1,0.1]}}, \n", 114 | " maxTime = 50)\n" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "By selecting just the state variables that we are interested in, we can observe with more clarity the evolution over time of the substrate and the product. " 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "int1 = model.integrate(showStateVars=['S','P'],initWidgets = {'\\k_c':[0.1, 0, 0.5, 0.1],\n", 131 | " '\\k_f':[1, 0, 10, 0.1],\n", 132 | " '\\k_r':[0.01, 0, 0.1, 0.01],\n", 133 | " 'initialState':{'S': [0.5,0,1,0.1],'E': [0.5,0,1,0.1],'P': [0,0,1,0.1],'C': [0,0,1,0.1]}}, \n", 134 | " maxTime = 50)\n" 135 | ] 136 | }, 137 | { 138 | "cell_type": "markdown", 139 | "metadata": {}, 140 | "source": [ 141 | "We can also analyse the dynamics using bifurcation analysis as well as looking at the vector field of the system, which allows us to see how all solutions develop over time. However, our model is a four-dimensional system, which unfortunately, prevents us from performing bifurcation analysis and vector analysis, since these are features supported only on lower-dimensional systems in MuMoT at present.\n", 142 | "\n", 143 | "We can however undertake more limited analyses of the full dynamics, and fuller analyses of reduced dynamics." 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "metadata": {}, 149 | "source": [ 150 | "##### The effects of noise" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "We can analyse the effects of noise in our model by making use of the Stochastic Simulation Algorithm (SSA). The magnitude of noise depends on the system size, as usual." 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "metadata": {}, 164 | "outputs": [], 165 | "source": [ 166 | "mssa=model.SSA(initWidgets = {'\\k_c':[0.1, 0, 0.5, 0.1],\n", 167 | " '\\k_f':[1, 0, 10, 0.1],\n", 168 | " '\\k_r':[0.01, 0, 0.1, 0.01],\n", 169 | " 'systemSize':[10,1,100,1],\n", 170 | " 'initialState':{'S': [0.5,0,1,0.1],'E': [0.5,0,1,0.1],'P': [0,0,1,0.1],'C': [0,0,1,0.1]}}, \n", 171 | " maxTime = 50, runs = 20,\n", 172 | " choose_yrange=[0,5.5])" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "#### Michaelis-Menten reduced dynamics \n", 180 | "\n", 181 | "In order to make use of some of MuMoT's feature we require a model with lower dimensions. Palsson ([2011](#references), p.81) shows how the original model can be analysed in terms of just $S$ and $C$ by using the relationship for the total amount of enzyme:\n", 182 | "\n", 183 | " $E_{o} = E + C$\n", 184 | " \n", 185 | "By replacing $C$ in the equations of $S$ and $X$ we can obtain a two dimensional model that follows the dynamics of these two components in the same way our four-dimensional model did." 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [ 194 | "model_reduced = mumot.parseModel(r\"\"\"\n", 195 | "C + S -> S + S: k_f\n", 196 | "C -> S: k_r\n", 197 | "C -> \\emptyset: k_c\n", 198 | "S -> C: k_f*e_o\n", 199 | "\"\"\")" 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "execution_count": null, 205 | "metadata": {}, 206 | "outputs": [], 207 | "source": [ 208 | "model_reduced.showODEs()" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "metadata": {}, 215 | "outputs": [], 216 | "source": [ 217 | "int1 = model_reduced.integrate(initWidgets = {'\\k_c':[0.1, 0, 0.5, 0.1],\n", 218 | " '\\k_f':[1, 0, 10, 0.1],\n", 219 | " '\\k_r':[0.01, 0, 0.1, 0.01],\n", 220 | " '\\e_o':[0.5, 0, 1, 0.1],\n", 221 | " 'initialState':{'S': [0.5,0,1,0.1],'C': [0.5,0,1,0.1]}}, \n", 222 | " maxTime = 50)\n" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": {}, 228 | "source": [ 229 | "The `stream` function displays how, for our model, only a single solution exists at the origin, since both the substrate $S$ and the complex $C$ are depleted by the reaction. " 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "vector1 = model_reduced.stream('S', 'C', showFixedPoints = True, initWidgets = {'\\k_c':[0.1, 0, 0.5, 0.1],\n", 239 | " '\\k_f':[1, 0, 10, 0.1],\n", 240 | " '\\k_r':[0.01, 0, 10, 0.01],\n", 241 | " '\\e_o':[0.5, 0, 1, 0.1],\n", 242 | " 'initialState':{'S': [0.5,0,1,0.1],'C': [0,0,1,0.1]}})" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "##### The effects of noise\n", 250 | "\n", 251 | "As before, we can analyse the effects of noise in our system." 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": null, 257 | "metadata": {}, 258 | "outputs": [], 259 | "source": [ 260 | "mssa_red=model_reduced.SSA(initWidgets = {'\\k_c':[0.1, 0, 0.5, 0.1],\n", 261 | " '\\k_f':[1, 0, 10, 0.1],\n", 262 | " '\\k_r':[0.01, 0, 0.1, 0.01],\n", 263 | " '\\e_o':[0.5, 0, 1, 0.1],\n", 264 | " 'initialState':{'C': [1,0,1,0.1]},'S': [1,0,1,0.1]}, \n", 265 | " maxTime = 50, runs=20)" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "### References\n", 273 | "\n", 274 | "\n", 275 | "* Michaelis L., Menten M. L. (1913) Die kinetik der invertinwirkung. _Biochemistry Z_ **49**, 333-369.\n", 276 | "* Murray, J. D. (2002). _Mathematical Biology I: An Introduction_. 3rd edition. Springer-Verlag.\n", 277 | "* Michaelis, L., Menten, M. L., Johnson, K. A., & Goody, R. S. (2011). The original Michaelis constant: translation of the 1913 Michaelis-Menten paper. _Biochemistry_ **50**(39), 8264–8269. https://doi.org/10.1021/bi201284u\n", 278 | "* Palsson, B. (2011). Enzyme kinetics. In: _Systems Biology: Simulation of Dynamic Network States_ (pp. 80-81). Cambridge University Press. https://doi.org/10.1017/CBO9780511736179.007" 279 | ] 280 | } 281 | ], 282 | "metadata": { 283 | "kernelspec": { 284 | "display_name": "Python 3", 285 | "language": "python", 286 | "name": "python3" 287 | }, 288 | "language_info": { 289 | "codemirror_mode": { 290 | "name": "ipython", 291 | "version": 3 292 | }, 293 | "file_extension": ".py", 294 | "mimetype": "text/x-python", 295 | "name": "python", 296 | "nbconvert_exporter": "python", 297 | "pygments_lexer": "ipython3", 298 | "version": "3.7.6" 299 | }, 300 | "toc": { 301 | "base_numbering": 1, 302 | "nav_menu": {}, 303 | "number_sections": true, 304 | "sideBar": true, 305 | "skip_h1_title": false, 306 | "title_cell": "Table of Contents", 307 | "title_sidebar": "Contents", 308 | "toc_cell": false, 309 | "toc_position": {}, 310 | "toc_section_display": true, 311 | "toc_window_display": false 312 | } 313 | }, 314 | "nbformat": 4, 315 | "nbformat_minor": 2 316 | } 317 | -------------------------------------------------------------------------------- /DemoNotebooks/Variance_suppression.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# MuMoT Demonstration Notebook: Variance-Suppression through Negative Feedback \n", 8 | "\n", 9 | "## Multiscale Modelling Tool \n", 10 | "\n", 11 | "*Andreagiovanni Reina, Department of Computer Science, University of Sheffield;\n", 12 | "James A. R. Marshall, Department of Computer Science, University of Sheffield*\n", 13 | "\n", 14 | "# Introduction\n", 15 | " \n", 16 | "This notebook reproduces results representative of those presented in Reina & Marshall ([2020](#references)), in which it is shown that negative feedback can acts as a mechanism to suppress stochastic fluctuations in a simple collective foraging model." 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import mumot" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "# Model Definitions\n", 33 | "\n", 34 | "We start with a full model of collective foraging that includes positive feedback, through recruitment, and negative, inhibitory feedback.\n", 35 | "Taking collective foraging by honeybees as an exemplar, positive feedback through recruitment occurs via the waggle dance (von Frisch, [1967](#references)), while negative feedback occurs via the stop signal (Nieh, [1993](#references))." 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "%%model\n", 45 | "$\n", 46 | "U -> A : q_A\n", 47 | "U -> B : q_B\n", 48 | "A -> U : a\n", 49 | "B -> U : a\n", 50 | "A + U -> A + A : r_a\n", 51 | "B + U -> B + B : r_b\n", 52 | "A + A -> A + U : z\n", 53 | "B + B -> B + U : z\n", 54 | "$" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "fullmodel = mumot.parseModel(In[2])\n", 64 | "fullmodel.show()" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "We next set a finite system size $N$, since on a short timescale there is a constant pool of foragers. This reduces the system to two ODEs:" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "fullmodel = fullmodel.substitute('U = N - A - B')\n", 81 | "fullmodel.showODEs()" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "Next we 'turn-off' the negative social feedback part of the model ($i.e.$ parameter $z=0$) to give a foraging model with only quality-independent positive feedback *r*. This is our **model without negative social feedback**:" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "posmodel = fullmodel.substitute('z = 0, r_a = r, r_b = r')\n", 98 | "posmodel.showODEs()" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "Instead, the **model with negative social feedback** has $z>0$ and quality-dependent positive social feedback (with strength $\\rho$):" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "negmodel = fullmodel.substitute('r_a = \\\\rho q_A, r_b = \\\\rho q_B')\n", 115 | "negmodel.showODEs()" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": {}, 121 | "source": [ 122 | "# Results\n", 123 | "\n", 124 | "In the following, we analyse the models using both infinite-population ODEs, and finite population stochastic simulations.\n", 125 | "\n", 126 | "## Model without negative social feedback\n", 127 | "\n", 128 | "The analysis of the deterministic ODE system predicts that a population starting from a fully uncommmitted initial state ($i.e.$ all individuals at time $t=0$ are in uncommitted state $U$) will converge to the distribution:\n", 129 | "\n", 130 | "$ A = q_A / (q_A + q_B) $\n", 131 | "\n", 132 | "$ B = q_B / (q_A + q_B) $" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": null, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "int1 = posmodel.integrate(initialState = {'B': 0.0, 'U': 1.0, 'A': 0.0},\n", 142 | " initWidgets={'a':[0.001, 0, 1, 0.001],'r':[100, 0, 200, 1],\n", 143 | " 'q_{A}':[0.8, 0, 1, 0.05],'q_{B}':[0.4, 0, 1, 0.05],\n", 144 | " 'maxTime':[0.5,0.1,2,0.1]},\n", 145 | " choose_yrange=[0,1], plotProportions=True, ylab=\"Subpopulations $x_i$\")" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "metadata": {}, 151 | "source": [ 152 | "## Model with negative social feedback\n", 153 | "\n", 154 | "Also for the case of the system with negative social feedback, the analysis of the deterministic ODE system predicts that a population starting from a fully uncommmitted initial state ($i.e.$ all individuals at time $t=0$ are in uncommitted state $U$) will converge to the distribution with a larger convergence time:\n", 155 | "\n", 156 | "$ A = q_A / (q_A + q_B) $\n", 157 | "\n", 158 | "$ B = q_B / (q_A + q_B) $" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "int2 = negmodel.integrate(initialState = {'B': 0.0, 'U': 1.0, 'A': 0.0},\n", 168 | " initWidgets={'a':[0.001, 0, 1, 0.001],'\\\\rho':[200, 0, 400, 1],\n", 169 | " 'q_{A}':[0.8, 0, 1, 0.05],'q_{B}':[0.4, 0, 1, 0.05],\n", 170 | " 'z':[3.4, 0, 50, 0.02],'maxTime':[10,1,1000,1]},\n", 171 | " choose_yrange=[0,1], plotProportions=True, ylab=\"Subpopulations $x_i$\")" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "## Noise and variance\n", 179 | "\n", 180 | "By introducing finite-system noise (via SSA), the analysis shows that the model **without** negative social feedback has a large variance around the dynamics predicted by the ODEs" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "mc1 = mumot.MuMoTmultiController([posmodel.SSA(silent=True), posmodel.integrate(silent=True)],\n", 190 | " shareAxes=True,\n", 191 | " initialState = {'B': 0.0, 'U': 1.0, 'A': 0.0}, \n", 192 | " initWidgets={'a':[0.001, 0, 1, 0.001],'r':[100, 0, 200, 1],\n", 193 | " 'q_{A}':[0.8, 0, 1, 0.05],'q_{B}':[0.4, 0, 1, 0.05],\n", 194 | " 'maxTime':[2,0.1,2,0.1],'systemSize':[200,10,1000,10],\n", 195 | " 'runs':[50,1,100,1]},\n", 196 | " choose_yrange=[0,1], ylab=\"Subpopulations $x_i$\")" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": {}, 202 | "source": [ 203 | "Instead, the model **with** negative social feedback has a very small variance around the dynamics predicted by the ODEs" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "metadata": {}, 210 | "outputs": [], 211 | "source": [ 212 | "mc2 = mumot.MuMoTmultiController([negmodel.SSA(silent=True), negmodel.integrate(silent=True)],\n", 213 | " shareAxes=True,\n", 214 | " initialState = {'B': 0.0, 'U': 1.0, 'A': 0.0}, \n", 215 | " initWidgets={'a':[0.001, 0, 1, 0.001],'rho':[200, 0, 400, 1],\n", 216 | " 'q_{A}':[0.8, 0, 1, 0.05],'q_{B}':[0.4, 0, 1, 0.05],\n", 217 | " 'z':[3.4, 0, 50, 0.02],'maxTime':[10,1,1000,1],\n", 218 | " 'systemSize':[200,10,1000,10],'runs':[10,1,100,1]},\n", 219 | " choose_yrange=[0,1], ylab=\"Subpopulations $x_i$\")" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "Returning to the analysis of the ODE system, we see that the model without negative social feedback has very slow dynamics if the initial point (at $t=0$) is different from $U=1$, which can be caused by initial random fluctuations:" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "metadata": {}, 233 | "outputs": [], 234 | "source": [ 235 | "int3 = posmodel.integrate(initWidgets={'initialState':{'B':[0.1,0,1,0.05], 'U':[0.8,0,1,0.05], 'A':[0.1,0,1,0.05]},\n", 236 | " 'a':[0.001, 0, 1, 0.001],'r':[100, 0, 200, 1],\n", 237 | " 'q_{A}':[0.8, 0, 1, 0.05],'q_{B}':[0.4, 0, 1, 0.05],\n", 238 | " 'maxTime':[300000,1,1000000,1]},\n", 239 | " choose_yrange=[0,1], plotProportions=True, ylab=\"Subpopulations $x_i$\")" 240 | ] 241 | }, 242 | { 243 | "cell_type": "markdown", 244 | "metadata": {}, 245 | "source": [ 246 | "Instead the system with negative social feedback has dynamics with speed independed from the initial starting point." 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": null, 252 | "metadata": {}, 253 | "outputs": [], 254 | "source": [ 255 | "int4 = negmodel.integrate(initWidgets={'initialState':{'B':[0.1,0,1,0.05], 'U':[0.8,0,1,0.05], 'A':[0.1,0,1,0.05]},\n", 256 | " 'a':[0.001, 0, 1, 0.001],'\\\\rho':[200, 0, 400, 1],\n", 257 | " 'q_{A}':[0.8, 0, 1, 0.05],'q_{B}':[0.4, 0, 1, 0.05],\n", 258 | " 'z':[3.4, 0, 50, 0.02],'maxTime':[5,1,100,1]},\n", 259 | " choose_yrange=[0,1], plotProportions=True, ylab=\"Subpopulations $x_i$\")" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "# Fokker-Planck equations and noise variance\n", 267 | "\n", 268 | "MuMoT allows you to easily compute the Fokker-Planck equations for both models and display the first-order and second-order moments of the system's noise." 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "metadata": {}, 275 | "outputs": [], 276 | "source": [ 277 | "posmodel.showFokkerPlanckEquation()" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": null, 283 | "metadata": {}, 284 | "outputs": [], 285 | "source": [ 286 | "negmodel.showFokkerPlanckEquation()" 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "metadata": {}, 292 | "source": [ 293 | "## Noise equations\n", 294 | "\n", 295 | "Having the Fokker-Planck equations allows us to derive the equations of motion for the fluctuations.\n", 296 | "\n", 297 | "### Model without negative social feedback" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": null, 303 | "metadata": {}, 304 | "outputs": [], 305 | "source": [ 306 | "posmodel.showNoiseEquations()" 307 | ] 308 | }, 309 | { 310 | "cell_type": "markdown", 311 | "metadata": {}, 312 | "source": [ 313 | "Because MuMoT allows you to access the `sympy` equations, we apply some simplification to the equations in order to find the solution to $\\langle \\eta_{A}^2 \\rangle$ at convergence ($t\\rightarrow \\infty$). We assume that $a\\approx0$ and $\\Phi_U (t\\rightarrow \\infty) \\approx 0$." 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [ 322 | "noiseEqPos=posmodel.getNoiseEquations()" 323 | ] 324 | }, 325 | { 326 | "cell_type": "code", 327 | "execution_count": null, 328 | "metadata": {}, 329 | "outputs": [], 330 | "source": [ 331 | "#in order to handle sympy equations, some imports are necessary\n", 332 | "from sympy import (\n", 333 | " collect,\n", 334 | " default_sort_key,\n", 335 | " Derivative,\n", 336 | " lambdify,\n", 337 | " latex,\n", 338 | " linsolve,\n", 339 | " numbered_symbols,\n", 340 | " preview,\n", 341 | " simplify,\n", 342 | " solve,\n", 343 | " Symbol,\n", 344 | " symbols,\n", 345 | " Function\n", 346 | ")\n", 347 | "from IPython.display import display, Math" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": null, 353 | "metadata": {}, 354 | "outputs": [], 355 | "source": [ 356 | "simplifiedNoiseEqPos={}\n", 357 | "\n", 358 | "simplifiedNoiseEqPos['etaU2']=noiseEqPos[2][Function('M_2')(Symbol('eta_U')**2)].subs(Symbol('Phi_U'),0).subs(Symbol('a'),0)\n", 359 | "display(Math(latex(simplifiedNoiseEqPos['etaU2'])+'=0'))\n", 360 | "simplifiedNoiseEqPos['etaU2']=solve( simplifiedNoiseEqPos['etaU2'], Function('M_2')(Symbol('eta_U')**2) )[0]\n", 361 | "display(Math(latex( Function('M_2')(Symbol('eta_U')**2) ) + '=' + latex(simplifiedNoiseEqPos['etaU2']) ))\n", 362 | "\n", 363 | "simplifiedNoiseEqPos['etaA*etaU']=noiseEqPos[2][Function('M_2')(Symbol('eta_A')*Symbol('eta_U'))].subs(\n", 364 | " Function('M_2')(Symbol('eta_U')**2),simplifiedNoiseEqPos['etaU2']).subs(Symbol('Phi_U'),0).subs(Symbol('a'),0)\n", 365 | "display(Math(latex(simplifiedNoiseEqPos['etaA*etaU'])+'=0'))\n", 366 | "simplifiedNoiseEqPos['etaA*etaU']=solve( simplifiedNoiseEqPos['etaA*etaU'], Function('M_2')(Symbol('eta_A')*Symbol('eta_U')) )[0]\n", 367 | "display(Math(latex( Function('M_2')(Symbol('eta_A')*Symbol('eta_U')) ) + '=' + latex(simplifiedNoiseEqPos['etaA*etaU']) ))\n", 368 | "\n", 369 | "simplifiedNoiseEqPos['etaA2']=noiseEqPos[2][Function('M_2')(Symbol('eta_A')**2)].subs(\n", 370 | " Function('M_2')(Symbol('eta_A')*Symbol('eta_U')),simplifiedNoiseEqPos['etaA*etaU']).subs(Symbol('Phi_U'),0)\n", 371 | "display(Math(latex(simplifiedNoiseEqPos['etaA2'])+'=0'))\n", 372 | "simplifiedNoiseEqPos['etaA2']=solve( simplifiedNoiseEqPos['etaA2'], Function('M_2')(Symbol('eta_A')**2) )[0]\n", 373 | "display(Math(latex( Function('M_2')(Symbol('eta_A')**2) ) + '=' + latex(simplifiedNoiseEqPos['etaA2']) ))" 374 | ] 375 | }, 376 | { 377 | "cell_type": "markdown", 378 | "metadata": {}, 379 | "source": [ 380 | "### Model with negative social feedback" 381 | ] 382 | }, 383 | { 384 | "cell_type": "code", 385 | "execution_count": null, 386 | "metadata": {}, 387 | "outputs": [], 388 | "source": [ 389 | "negmodel.showNoiseEquations()" 390 | ] 391 | }, 392 | { 393 | "cell_type": "code", 394 | "execution_count": null, 395 | "metadata": {}, 396 | "outputs": [], 397 | "source": [ 398 | "noiseEqNeg=negmodel.getNoiseEquations()" 399 | ] 400 | }, 401 | { 402 | "cell_type": "markdown", 403 | "metadata": {}, 404 | "source": [ 405 | "For the system with negative feedback, we assume that $\\Phi_U (t\\rightarrow \\infty) \\approx 0$, $a\\approx0$, and $\\langle \\eta_{A}\\eta_{U} \\rangle \\approx 0$ to find the solution to $\\langle \\eta_{A}^2 \\rangle$:" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": null, 411 | "metadata": {}, 412 | "outputs": [], 413 | "source": [ 414 | "simplifiedNoiseEqNeg=noiseEqNeg[2][Function('M_2')(Symbol('eta_A')**2)].subs(Symbol('Phi_U'),0).subs(\n", 415 | " Function('M_2')(Symbol('eta_A')*Symbol('eta_U')),0).subs(Symbol('a'),0)\n", 416 | "display(Math(latex(simplifiedNoiseEqNeg)+'=0'))\n", 417 | "simplifiedNoiseEqNeg=solve( simplifiedNoiseEqNeg, Function('M_2')(Symbol('eta_A')**2) )[0]\n", 418 | "display(Math(latex( Function('M_2')(Symbol('eta_A')**2) ) + '=' + latex(simplifiedNoiseEqNeg) ))" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": {}, 424 | "source": [ 425 | "# References \n", 426 | "\n", 427 | "" 432 | ] 433 | } 434 | ], 435 | "metadata": { 436 | "kernelspec": { 437 | "display_name": "Python 3", 438 | "language": "python", 439 | "name": "python3" 440 | }, 441 | "language_info": { 442 | "codemirror_mode": { 443 | "name": "ipython", 444 | "version": 3 445 | }, 446 | "file_extension": ".py", 447 | "mimetype": "text/x-python", 448 | "name": "python", 449 | "nbconvert_exporter": "python", 450 | "pygments_lexer": "ipython3", 451 | "version": "3.7.7" 452 | }, 453 | "toc": { 454 | "base_numbering": 1, 455 | "nav_menu": {}, 456 | "number_sections": true, 457 | "sideBar": true, 458 | "skip_h1_title": false, 459 | "title_cell": "Table of Contents", 460 | "title_sidebar": "Contents", 461 | "toc_cell": false, 462 | "toc_position": {}, 463 | "toc_section_display": true, 464 | "toc_window_display": true 465 | } 466 | }, 467 | "nbformat": 4, 468 | "nbformat_minor": 2 469 | } 470 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MuMoT: Multiscale Modelling Tool 2 | 3 | [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/DiODeProject/MuMoT/v1.2.2?filepath=docs%2FMuMoTuserManual.ipynb) 4 | [![](https://img.shields.io/pypi/v/mumot.svg)](https://pypi.org/pypi/mumot/) 5 | [![](https://img.shields.io/pypi/pyversions/mumot.svg)](https://pypi.org/pypi/mumot/) 6 | [![](https://img.shields.io/pypi/l/mumot.svg)](https://pypi.org/pypi/mumot/) 7 | 8 | ![test_and_upload_distribution](https://github.com/DiODeProject/MuMoT/workflows/test_and_upload_distribution/badge.svg?branch=master) 9 | [![Documentation Status](https://readthedocs.org/projects/mumot/badge/?version=latest)](https://mumot.readthedocs.io/en/latest/?badge=latest) 10 | [![Coverage Status](https://codecov.io/github/DiODeProject/MuMoT/coverage.svg?branch=master)](https://codecov.io/gh/DiODeProject/MuMoT) 11 | 12 | ## Introduction / overview 13 | 14 | MuMoT (Multiscale Modelling Tool) is a tool designed to allow sophisticated mathematical modelling and analysis, 15 | without writing equations - 16 | the class of models that can be represented is broad, 17 | ranging from chemical reaction kinetics to 18 | demography and collective behaviour - 19 | by using a web-based interactive interface with minimal coding, 20 | rapid development and exploration of models is facilitated - 21 | the tool may also be particularly useful for pedagogical demonstrations. 22 | 23 | ## Documentation 24 | 25 | See [https://mumot.readthedocs.io](https://mumot.readthedocs.io) for 26 | 27 | * How to **get started** with MuMoT, 28 | including how to **use MuMoT online** without installing anything on your own machine. 29 | * **Installation instructions**, should you want to install it on your own machine. 30 | * **Development**: how to report issues and how to contribute to the project. 31 | * Information about MuMoT including contributors, acknowledgements, and citation details. 32 | * API documentation. 33 | -------------------------------------------------------------------------------- /TestNotebooks/MiscTests/MuMoTtest_1dstream.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import mumot\n", 10 | "\n", 11 | "mumot.about()" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "model1 = mumot.parseModel(r\"\"\"\n", 21 | "U -> A : g_1\n", 22 | "A -> U : a_1\n", 23 | "A + U -> A + A : r_1\n", 24 | "\"\"\")" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "model1.showODEs()" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "model2 = model1.substitute('U = N - A')" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "modelStreamPlot = model2.stream('A', showFixedPoints = True)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [] 60 | } 61 | ], 62 | "metadata": { 63 | "kernelspec": { 64 | "display_name": "Python 3", 65 | "language": "python", 66 | "name": "python3" 67 | }, 68 | "language_info": { 69 | "codemirror_mode": { 70 | "name": "ipython", 71 | "version": 3 72 | }, 73 | "file_extension": ".py", 74 | "mimetype": "text/x-python", 75 | "name": "python", 76 | "nbconvert_exporter": "python", 77 | "pygments_lexer": "ipython3", 78 | "version": "3.6.8" 79 | }, 80 | "toc": { 81 | "base_numbering": 1, 82 | "nav_menu": {}, 83 | "number_sections": true, 84 | "sideBar": true, 85 | "skip_h1_title": false, 86 | "title_cell": "Table of Contents", 87 | "title_sidebar": "Contents", 88 | "toc_cell": false, 89 | "toc_position": {}, 90 | "toc_section_display": true, 91 | "toc_window_display": false 92 | } 93 | }, 94 | "nbformat": 4, 95 | "nbformat_minor": 2 96 | } 97 | -------------------------------------------------------------------------------- /TestNotebooks/MiscTests/MuMoTtest_3dstream.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# This notebook provides a quick test for 3D stream plots in MuMot" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import mumot\n", 17 | "\n", 18 | "mumot.__version__" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## Here we define our 3D model:" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "model1 = mumot.parseModel(r\"\"\"\n", 35 | "U -> A : g_1\n", 36 | "U -> B : g_2\n", 37 | "U -> C : g_3\n", 38 | "A -> U : a_1\n", 39 | "B -> U : a_2\n", 40 | "C -> U : a_3\n", 41 | "A + U -> A + A : r_1\n", 42 | "B + U -> B + B : r_2\n", 43 | "C + U -> C + C : r_3\n", 44 | "A + B -> A + U : s\n", 45 | "A + B -> B + U : s\n", 46 | "A + C -> A + U : s\n", 47 | "A + C -> C + U : s\n", 48 | "B + C -> B + U : s\n", 49 | "B + C -> C + U : s\n", 50 | "\"\"\")" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "model2 = model1.substitute('U = N - A - B - C')" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "3d stream plot takes a random selection of starting points and plots streams by integrating the model equations using the start points as initial conditions." 67 | ] 68 | }, 69 | { 70 | "cell_type": "markdown", 71 | "metadata": {}, 72 | "source": [ 73 | "The colour of each stream shows the initial speed of each stream in the stream plot." 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "metadata": {}, 79 | "source": [ 80 | "New keywords for the 3d stream plot: \n", 81 | " - setNumPoints (type: int) this sets the number of streams to be plotted\n", 82 | " - maxTime (type: float) this sets the length of time over which the streams are integrated" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "modelStreamPlot = model2.stream('A', 'B', 'C', showFixedPoints = True, maxTime = 2.0)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "modelStreamPlot.showLogs()" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "NOTE: streams can be slow to update when the sliders are changed, if showFixedPoints = True then sometimes it can get stuck on calculating the fixed points and won't update" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [] 116 | } 117 | ], 118 | "metadata": { 119 | "kernelspec": { 120 | "display_name": "Python 3", 121 | "language": "python", 122 | "name": "python3" 123 | }, 124 | "language_info": { 125 | "codemirror_mode": { 126 | "name": "ipython", 127 | "version": 3 128 | }, 129 | "file_extension": ".py", 130 | "mimetype": "text/x-python", 131 | "name": "python", 132 | "nbconvert_exporter": "python", 133 | "pygments_lexer": "ipython3", 134 | "version": "3.6.8" 135 | }, 136 | "toc": { 137 | "base_numbering": 1, 138 | "nav_menu": {}, 139 | "number_sections": true, 140 | "sideBar": true, 141 | "skip_h1_title": false, 142 | "title_cell": "Table of Contents", 143 | "title_sidebar": "Contents", 144 | "toc_cell": false, 145 | "toc_position": {}, 146 | "toc_section_display": true, 147 | "toc_window_display": false 148 | } 149 | }, 150 | "nbformat": 4, 151 | "nbformat_minor": 2 152 | } 153 | -------------------------------------------------------------------------------- /TestNotebooks/MiscTests/MuMoTtest_GreekLetters.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# MuMoT test notebook for using letters from the Greek alphabet" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "This notebook tests some basic functionality of MuMoT when reactants and rates are Greek letters (with Greek indices). The analysis is based on the honeybee stop-signal model (Seeley et al. (2012) & Pais et al. (2013)) studied in more detail in the user manual. " 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": {}, 21 | "outputs": [], 22 | "source": [ 23 | "import mumot\n", 24 | "\n", 25 | "mumot.__version__" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "model1 = mumot.parseModel(r\"\"\"\n", 35 | "U -> \\alpha : g_1\n", 36 | "U -> \\Gamma_\\beta : g_2\n", 37 | "\\alpha -> U : a_1\n", 38 | "\\Gamma_\\beta -> U : a_2\n", 39 | "\\alpha + U -> \\alpha + \\alpha : r_1\n", 40 | "\\Gamma_\\beta + U -> \\Gamma_\\beta + \\Gamma_\\beta : r_2\n", 41 | "\\alpha + \\Gamma_\\beta -> \\alpha + U : \\sigma\n", 42 | "\\alpha + \\Gamma_\\beta -> \\Gamma_\\beta + U : \\sigma\n", 43 | "\"\"\")" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "int1 = model1.integrate(showStateVars=['\\\\alpha', '\\\\Gamma_\\\\beta', 'U'],\n", 53 | " initWidgets={'maxTime':[10,5,50,1], \n", 54 | " 'initialState':{'U': [0.5,0,1,0.01],'\\\\Gamma_\\\\beta': [0.5,0,1,0.1],'\\\\alpha': [0,0,1,0.1]},\n", 55 | " 'g_{1}':[0.5,0,1,0.01]},\n", 56 | " \n", 57 | " conserved=True)" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": {}, 64 | "outputs": [], 65 | "source": [ 66 | "int1.showLogs()" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "metadata": {}, 73 | "outputs": [], 74 | "source": [ 75 | "model2 = model1.substitute('a_1 = 1/v_1, a_2 = 1/v_2, g_1 = v_1, g_2 = v_2, r_1 = v_1, r_2 = v_2')" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "model3 = model2.substitute('v_1 = \\\\mu + \\\\Delta/2, v_2 = \\\\mu - \\\\Delta/2')" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": null, 90 | "metadata": {}, 91 | "outputs": [], 92 | "source": [ 93 | "model3.showODEs()" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": null, 99 | "metadata": {}, 100 | "outputs": [], 101 | "source": [ 102 | "model3.show()" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": null, 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "model4 = model3.substitute('U = N - \\\\alpha - \\\\Gamma_\\\\beta')" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "modelBifCont1 = model4.bifurcation('\\\\sigma','\\\\alpha-\\\\Gamma_\\\\beta', \n", 121 | " initWidgets={'mu':[3, 1, 5, 0.5], 'Delta':[0, 0, 2, 0.1], \n", 122 | " 'initBifParam':[4.8, 3, 5, 0.1]},\n", 123 | " choose_xrange=[0, 5])" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "modelStreamCont1 = model4.stream('\\\\alpha', '\\\\Gamma_\\\\beta',fontsize=25, xlab=r'this is the x-label', \n", 133 | " showFixedPoints=False, showNoise=False)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": null, 139 | "metadata": {}, 140 | "outputs": [], 141 | "source": [ 142 | "model4.SSA()" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "model4.showODEs(method='vanKampen')" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": null, 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [ 160 | "model4.showMasterEquation()" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "model4.showFokkerPlanckEquation()" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": null, 175 | "metadata": { 176 | "scrolled": false 177 | }, 178 | "outputs": [], 179 | "source": [ 180 | "model4.showVanKampenExpansion()" 181 | ] 182 | } 183 | ], 184 | "metadata": { 185 | "kernelspec": { 186 | "display_name": "Python 3", 187 | "language": "python", 188 | "name": "python3" 189 | }, 190 | "language_info": { 191 | "codemirror_mode": { 192 | "name": "ipython", 193 | "version": 3 194 | }, 195 | "file_extension": ".py", 196 | "mimetype": "text/x-python", 197 | "name": "python", 198 | "nbconvert_exporter": "python", 199 | "pygments_lexer": "ipython3", 200 | "version": "3.5.4" 201 | }, 202 | "toc": { 203 | "base_numbering": 1, 204 | "nav_menu": {}, 205 | "number_sections": true, 206 | "sideBar": true, 207 | "skip_h1_title": false, 208 | "title_cell": "Table of Contents", 209 | "title_sidebar": "Contents", 210 | "toc_cell": false, 211 | "toc_position": {}, 212 | "toc_section_display": true, 213 | "toc_window_display": false 214 | } 215 | }, 216 | "nbformat": 4, 217 | "nbformat_minor": 2 218 | } 219 | -------------------------------------------------------------------------------- /TestNotebooks/MiscTests/MuMoTtest_MasterEq.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# This notebook tests the derivation of the Master equation and related functionality in MuMoT. " 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import mumot\n", 17 | "\n", 18 | "mumot.__version__" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "%%model\n", 28 | "$\n", 29 | "A -> B : k_1\n", 30 | "B -> A : k_2\n", 31 | "$" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "model1 = mumot.parseModel(In[2])" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": null, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "model1.showNoiseEquations()" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "model1.showNoiseSolutions()" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": null, 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "model1.showStoichiometry()" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": null, 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "model1.showODEs()" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "model1.showMasterEquation()" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": {}, 92 | "outputs": [], 93 | "source": [ 94 | "model1.showVanKampenExpansion()" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "model1.showODEs(method='vanKampen')" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "model1.showFokkerPlanckEquation()" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "metadata": {}, 119 | "outputs": [], 120 | "source": [ 121 | "%%model\n", 122 | "$\n", 123 | "U -> A : g_1\n", 124 | "U -> B : g_2\n", 125 | "A -> U : a_1\n", 126 | "B -> U : a_2\n", 127 | "A + U -> A + A : r_1\n", 128 | "B + U -> B + B : r_2\n", 129 | "A + B -> A + U : s_1\n", 130 | "A + B -> B + U : s_2\n", 131 | "$" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "model2 = mumot.parseModel(In[12])" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": null, 146 | "metadata": {}, 147 | "outputs": [], 148 | "source": [ 149 | "model2.showNoiseEquations()" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "model2.showNoiseSolutions()" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": null, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "model2.showODEs()" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "metadata": {}, 174 | "outputs": [], 175 | "source": [ 176 | "model2.showStoichiometry()" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "model2.showMasterEquation()" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [ 194 | "model2.showVanKampenExpansion()" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": null, 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "model2.showODEs(method='vanKampen')" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "metadata": {}, 210 | "outputs": [], 211 | "source": [ 212 | "model2.showFokkerPlanckEquation()" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": null, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "model3 = model2.substitute('a_1 = 1/v_1, a_2 = 1/v_2, g_1 = v_1, g_2 = v_2, r_1 = v_1, r_2 = v_2')" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "model3.showNoiseEquations()" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [ 239 | "model3.showRates()" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": null, 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "model3.showReactants()" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": {}, 255 | "outputs": [], 256 | "source": [ 257 | "model3.showODEs()" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": null, 263 | "metadata": {}, 264 | "outputs": [], 265 | "source": [ 266 | "model3.showStoichiometry()" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": null, 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "model3.showMasterEquation()" 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": null, 281 | "metadata": {}, 282 | "outputs": [], 283 | "source": [ 284 | "model3.showVanKampenExpansion()" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": null, 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "model3.showODEs(method='vanKampen')" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "metadata": {}, 300 | "outputs": [], 301 | "source": [ 302 | "model3.showFokkerPlanckEquation()" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "metadata": {}, 309 | "outputs": [], 310 | "source": [ 311 | "%%model\n", 312 | "$\n", 313 | "U -> A : g_1\n", 314 | "U -> B : g_2\n", 315 | "U -> C : g_3\n", 316 | "A -> U : a_1\n", 317 | "B -> U : a_2\n", 318 | "C -> U : a_3\n", 319 | "A + U -> A + A : r_1\n", 320 | "B + U -> B + B : r_2\n", 321 | "C + U -> C + C : r_2\n", 322 | "A + B -> A + U : s_1\n", 323 | "A + B -> B + U : s_2\n", 324 | "A + C -> A + U : s_3\n", 325 | "A + C -> C + U : s_4\n", 326 | "B + C -> B + U : s_5\n", 327 | "B + C -> C + U : s_6\n", 328 | "$" 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": null, 334 | "metadata": {}, 335 | "outputs": [], 336 | "source": [ 337 | "model4 = mumot.parseModel(In[44])" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": null, 343 | "metadata": {}, 344 | "outputs": [], 345 | "source": [ 346 | "model4.showNoiseEquations()" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": null, 352 | "metadata": {}, 353 | "outputs": [], 354 | "source": [ 355 | "model4.showRates()" 356 | ] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "execution_count": null, 361 | "metadata": {}, 362 | "outputs": [], 363 | "source": [ 364 | "model4.showReactants()" 365 | ] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "execution_count": null, 370 | "metadata": {}, 371 | "outputs": [], 372 | "source": [ 373 | "model4.showODEs()" 374 | ] 375 | }, 376 | { 377 | "cell_type": "code", 378 | "execution_count": null, 379 | "metadata": {}, 380 | "outputs": [], 381 | "source": [ 382 | "model4.showStoichiometry()" 383 | ] 384 | }, 385 | { 386 | "cell_type": "code", 387 | "execution_count": null, 388 | "metadata": {}, 389 | "outputs": [], 390 | "source": [ 391 | "model4.showMasterEquation()" 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "execution_count": null, 397 | "metadata": {}, 398 | "outputs": [], 399 | "source": [ 400 | "model4.showVanKampenExpansion()" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": null, 406 | "metadata": {}, 407 | "outputs": [], 408 | "source": [ 409 | "model4.showODEs(method='vanKampen')" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": null, 415 | "metadata": {}, 416 | "outputs": [], 417 | "source": [ 418 | "model4.showFokkerPlanckEquation()" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": null, 424 | "metadata": {}, 425 | "outputs": [], 426 | "source": [ 427 | "model5 = model4.substitute('s_1 = s, s_2 = s, s_3 = s, s_4 = s, s_5 = s, s_6 = s, U = N-A-B-C')" 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": null, 433 | "metadata": {}, 434 | "outputs": [], 435 | "source": [ 436 | "model5.showRates()" 437 | ] 438 | }, 439 | { 440 | "cell_type": "code", 441 | "execution_count": null, 442 | "metadata": {}, 443 | "outputs": [], 444 | "source": [ 445 | "model5.showReactants()" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": null, 451 | "metadata": {}, 452 | "outputs": [], 453 | "source": [ 454 | "model5.showODEs()" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": null, 460 | "metadata": {}, 461 | "outputs": [], 462 | "source": [ 463 | "model5.showStoichiometry()" 464 | ] 465 | }, 466 | { 467 | "cell_type": "code", 468 | "execution_count": null, 469 | "metadata": {}, 470 | "outputs": [], 471 | "source": [ 472 | "model5.showMasterEquation()" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": null, 478 | "metadata": {}, 479 | "outputs": [], 480 | "source": [ 481 | "model5.showVanKampenExpansion()" 482 | ] 483 | }, 484 | { 485 | "cell_type": "code", 486 | "execution_count": null, 487 | "metadata": {}, 488 | "outputs": [], 489 | "source": [ 490 | "model5.showODEs(method='vanKampen')" 491 | ] 492 | }, 493 | { 494 | "cell_type": "code", 495 | "execution_count": null, 496 | "metadata": {}, 497 | "outputs": [], 498 | "source": [ 499 | "model5.showFokkerPlanckEquation()" 500 | ] 501 | } 502 | ], 503 | "metadata": { 504 | "kernelspec": { 505 | "display_name": "Python 3", 506 | "language": "python", 507 | "name": "python3" 508 | }, 509 | "language_info": { 510 | "codemirror_mode": { 511 | "name": "ipython", 512 | "version": 3 513 | }, 514 | "file_extension": ".py", 515 | "mimetype": "text/x-python", 516 | "name": "python", 517 | "nbconvert_exporter": "python", 518 | "pygments_lexer": "ipython3", 519 | "version": "3.5.4" 520 | }, 521 | "toc": { 522 | "base_numbering": 1, 523 | "nav_menu": {}, 524 | "number_sections": true, 525 | "sideBar": true, 526 | "skip_h1_title": false, 527 | "title_cell": "Table of Contents", 528 | "title_sidebar": "Contents", 529 | "toc_cell": false, 530 | "toc_position": {}, 531 | "toc_section_display": true, 532 | "toc_window_display": false 533 | } 534 | }, 535 | "nbformat": 4, 536 | "nbformat_minor": 1 537 | } 538 | -------------------------------------------------------------------------------- /TestNotebooks/MiscTests/MuMoTtest_MultiController.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import mumot\n", 10 | "\n", 11 | "mumot.setVerboseExceptions()\n", 12 | "mumot.about()" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "model1 = mumot.parseModel(r\"\"\"\n", 22 | "U -> A : g_A\n", 23 | "U -> B : g_B\n", 24 | "A -> U : a_A\n", 25 | "B -> U : a_B\n", 26 | "A + U -> A + A : r_A\n", 27 | "B + U -> B + B : r_B\n", 28 | "A + B -> A + U : s\n", 29 | "A + B -> B + U : s\n", 30 | "\"\"\")" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": null, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "model2 = model1.substitute('U = N - A - B')" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "model3 = model2.substitute('a_A = 1/v_A, a_B = 1/v_B, g_A = v_A, g_B = v_B, r_A = v_A, r_B = v_B')" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "model4 = model3.substitute('v_A = \\mu + \\Delta/2, v_B = \\mu - \\Delta/2')" 58 | ] 59 | }, 60 | { 61 | "cell_type": "markdown", 62 | "metadata": {}, 63 | "source": [ 64 | "## view arguments set on creation (works)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "stream1 = model4.stream('A','B', showFixedPoints = True, silent = True)\n", 74 | "mc1 = mumot.MuMoTmultiController([stream1], params = [('s',1)])" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "## view arguments set at multicontroller level (doesn't work; view has already been created)" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": null, 87 | "metadata": {}, 88 | "outputs": [], 89 | "source": [ 90 | "stream2 = model4.stream('A','B', silent = True)\n", 91 | "mc2 = mumot.MuMoTmultiController([stream2], showFixedPoints = True)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": {}, 97 | "source": [ 98 | "## advanced options specified by keyword on creation (_i.e._ not via `initWidgets`)" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "ssa1 = model4.SSA(silent = True, visualisationType='final', final_x='A', final_y='B', params = [('s',1)])\n", 108 | "mc3 = mumot.MuMoTmultiController([ssa1])" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "## advanced options specified by keyword at multicontroller level (_i.e._ not via `initWidgets`) (works; controller is not created on creation of silent controller)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "ssa2 = model4.SSA(silent = True, params = [('s',1)])\n", 125 | "mc4 = mumot.MuMoTmultiController([ssa2], visualisationType='final', final_x='A', final_y='B')" 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": {}, 131 | "source": [ 132 | "## advanced options specified by keyword at multicontroller level (_i.e._ not via `initWidgets`) and parameters set via `params`" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": null, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "ssa3 = model4.SSA(silent = True)\n", 142 | "mc5 = mumot.MuMoTmultiController([ssa3], visualisationType='final', final_x='A', final_y='B', params = [('s',1)])" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "## advanced options specified by keyword on creation (_i.e._ not via `initWidgets`), parameters set via `params`, and advanced widgets set via `initWidgets`" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "ssa4 = model4.SSA(silent = True, visualisationType='final', final_x='A', final_y='B', aggregateResults = True, params = [('s',1)], initWidgets={'runs':[50,1,100,1]})\n", 159 | "mc6 = mumot.MuMoTmultiController([ssa4])" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "## advanced options specified by keyword on creation (_i.e._ not via `initWidgets`), parameters set via `params`, `initialState` set and advanced widgets set via `initWidgets` at multiController level; `params` should be specifiable either on creation or at multicontroller level" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": null, 172 | "metadata": {}, 173 | "outputs": [], 174 | "source": [ 175 | "#ssa5 = model4.SSA(silent = True, visualisationType='final', final_x='A', final_y='B', aggregateResults = True)\n", 176 | "#mc7 = mumot.MuMoTmultiController([ssa5], params = [('s',1)], initWidgets={'runs':[50,1,100,1]}, initialState = {'B': 0.0, 'U': 1.0, 'A': 0.0})" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "#ssa6 = model4.SSA(silent = True, visualisationType='final', final_x='A', final_y='B', aggregateResults = True, params = [('s',1)])\n", 186 | "#mc8 = mumot.MuMoTmultiController([ssa6], initWidgets={'runs':[50,1,100,1]}, initialState = {'B': 0.0, 'U': 1.0, 'A': 0.0})" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "## advanced test; different `params` for different constituent controllers" 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "metadata": {}, 200 | "outputs": [], 201 | "source": [ 202 | "stream3 = model4.stream('A','B', params = [('\\\\Delta', 0)], silent = True)\n", 203 | "stream4 = model4.stream('A','B', params = [('\\\\Delta', 1)], silent = True)\n", 204 | "mc9 = mumot.MuMoTmultiController([stream3, stream4], initWidgets={'s':[1,0,5,0.5]})" 205 | ] 206 | }, 207 | { 208 | "cell_type": "markdown", 209 | "metadata": {}, 210 | "source": [ 211 | "## very advanced test; different `params` for different constituent controllers, higher level common parameter as well" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": null, 217 | "metadata": {}, 218 | "outputs": [], 219 | "source": [ 220 | "stream5 = model4.stream('A','B', params = [('\\\\Delta', 0)], silent = True)\n", 221 | "stream6 = model4.stream('A','B', params = [('\\\\Delta', 1)], silent = True)\n", 222 | "mc10 = mumot.MuMoTmultiController([stream5, stream6], params = [('s', 0)])" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": {}, 228 | "source": [ 229 | "## very advanced test; different `initialState` for different constituent controllers" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "metadata": { 236 | "scrolled": false 237 | }, 238 | "outputs": [], 239 | "source": [ 240 | "multiagent1 = model4.multiagent(silent=True, visualisationType='graph', initialState = {'B': 0.0, 'U': 1.0, 'A': 0.0})\n", 241 | "ssa7 = model4.SSA(silent = True, visualisationType='final', maxTime=4, plotProportions=True, initialState = {'B': 0.0, 'U': 1.0, 'A': 0.0})\n", 242 | "mc12 = mumot.MuMoTmultiController([multiagent1, ssa7],\n", 243 | " params = [('\\\\Delta', 0.5), ('\\\\mu', 2.0), ('s', 0.5)],\n", 244 | " initWidgets={'runs':[1,1,50,1]},\n", 245 | " shareAxes = False)" 246 | ] 247 | } 248 | ], 249 | "metadata": { 250 | "kernelspec": { 251 | "display_name": "Python 3", 252 | "language": "python", 253 | "name": "python3" 254 | }, 255 | "language_info": { 256 | "codemirror_mode": { 257 | "name": "ipython", 258 | "version": 3 259 | }, 260 | "file_extension": ".py", 261 | "mimetype": "text/x-python", 262 | "name": "python", 263 | "nbconvert_exporter": "python", 264 | "pygments_lexer": "ipython3", 265 | "version": "3.6.8" 266 | }, 267 | "toc": { 268 | "base_numbering": 1, 269 | "nav_menu": {}, 270 | "number_sections": true, 271 | "sideBar": true, 272 | "skip_h1_title": false, 273 | "title_cell": "Table of Contents", 274 | "title_sidebar": "Contents", 275 | "toc_cell": false, 276 | "toc_position": {}, 277 | "toc_section_display": true, 278 | "toc_window_display": false 279 | } 280 | }, 281 | "nbformat": 4, 282 | "nbformat_minor": 2 283 | } 284 | -------------------------------------------------------------------------------- /TestNotebooks/MiscTests/MuMoTtest_NoiseFixedPoints.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# This notebook shows how to study noise with MuMoT\n", 8 | "There are 7 examples worked through below." 9 | ] 10 | }, 11 | { 12 | "cell_type": "code", 13 | "execution_count": null, 14 | "metadata": {}, 15 | "outputs": [], 16 | "source": [ 17 | "import mumot\n", 18 | "\n", 19 | "mumot.__version__\n", 20 | "mumot.about()" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "## Production and decay of protein $P$ and its dimerization into $P_2$ ($P_2$ is called $Q$ below)\n", 28 | "### (see F. Hayot & C. Jayaprakash (2004), Physical Biology 1, pp. 205-210)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "model1 = mumot.parseModel(r\"\"\"\n", 38 | "\\emptyset -> P : k_3\n", 39 | "P -> \\emptyset : k\n", 40 | "P + P -> Q + \\emptyset : k_1\n", 41 | "Q + \\emptyset -> P + P : k_2\n", 42 | "\"\"\")" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "model1.showODEs(method='vanKampen')" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "metadata": {}, 58 | "outputs": [], 59 | "source": [ 60 | "evol1 = model1.integrate(['Q', 'P'], legend_loc='center right')" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "evol1.showLogs(tail=True)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "bookmark_evol1 = model1.integrate(showStateVars=['P', 'Q'], initialState = {'P': 1.0, 'Q': 0.0}, \n", 79 | " maxTime = 3.0, plotProportions = False, \n", 80 | " params = [('k_{3}', 2.0), ('k_{2}', 2.0), ('k', 2.0), ('k_{1}', 2.0), \n", 81 | " ('plotLimits', 1), ('systemSize', 10.0)], \n", 82 | " legend_loc = 'center right', bookmark = False)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "Ncorr1 = model1.noiseCorrelations(maxTime=120, legend_loc='upper right', legend_fontsize=10)" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "Ncorr1.showLogs(tail=True)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "model1.noiseCorrelations()" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": null, 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "bookmark_ncorr1 = model1.noiseCorrelations(initialState = {'P': 1.0, 'Q': 0.0}, params = [('k_{3}', 2.0), ('k_{2}', 2.0), ('k', 2.0), ('k_{1}', 2.0), ('plotLimits', 1), ('systemSize', 10.0)], legend_loc = 'upper right', legend_fontsize = 10, maxTime = 120, bookmark = False)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "model1.showFokkerPlanckEquation()" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "model1.showNoiseEquations()" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "model1.showNoiseSolutions()" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "stream1 = model1.stream('P', 'Q', showFixedPoints=True, showNoise = True)" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "stream1.showLogs()" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "bookmark_stream1 = model1.stream('P', 'Q', params = [('k_{3}', 2.0), ('k_{2}', 2.0), ('k', 2.0), ('k_{1}', 2.0), ('plotLimits', 1.5), ('systemSize', 10.0)], showFixedPoints = True, showNoise = True, bookmark = False)" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "## The 'Brusselator' reaction mechanism (proposed by Prigogene and Lefever in 1968)\n", 180 | "### (see J.D. Murray (1991), Mathematical Biology, p. 175)" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "model2 = mumot.parseModel(r\"\"\"\n", 190 | "(\\alpha) -> X : \\gamma\n", 191 | "X + X + Y -> X + X + X : \\chi\n", 192 | "(\\beta) + X -> Y + \\emptyset : \\delta\n", 193 | "X -> \\emptyset : \\xi\n", 194 | "\"\"\")" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": null, 200 | "metadata": {}, 201 | "outputs": [], 202 | "source": [ 203 | "evol2 = model2.integrate(legend_loc='lower right')" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "metadata": {}, 210 | "outputs": [], 211 | "source": [ 212 | "evol2.showLogs()" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": null, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "evol2b = model2.integrate(['X'], maxTime=50, legend_loc='lower right')" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "Ncorr2 = model2.noiseCorrelations(maxTime=20, legend_loc='upper right', legend_fontsize=14)" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [ 239 | "Ncorr2.showLogs()" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": null, 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "model2.showFokkerPlanckEquation()" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": {}, 255 | "outputs": [], 256 | "source": [ 257 | "model2.showNoiseEquations()" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": null, 263 | "metadata": {}, 264 | "outputs": [], 265 | "source": [ 266 | "model2.showNoiseSolutions()" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": null, 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "stream2 = model2.stream('X', 'Y', showNoise=True, showFixedPoints=False)" 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": null, 281 | "metadata": {}, 282 | "outputs": [], 283 | "source": [ 284 | "stream2.showLogs()" 285 | ] 286 | }, 287 | { 288 | "cell_type": "markdown", 289 | "metadata": {}, 290 | "source": [ 291 | "## Textbook example for multivariate master equation \n", 292 | "### (see N. van Kampen (1981), Stochastic processes in physics and chemistry, pp. 273-277)" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": null, 298 | "metadata": {}, 299 | "outputs": [], 300 | "source": [ 301 | "model3 = mumot.parseModel(r\"\"\"\n", 302 | "(A) -> X : \\alpha\n", 303 | "X + X -> Y + \\emptyset : \\gamma\n", 304 | "Y -> (B) : \\beta\n", 305 | "\"\"\")" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "model3.showODEs(method='vanKampen')" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": null, 320 | "metadata": {}, 321 | "outputs": [], 322 | "source": [ 323 | "evol3 = model3.integrate(legend_loc='center right')" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": null, 329 | "metadata": {}, 330 | "outputs": [], 331 | "source": [ 332 | "evol3.showLogs()" 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "execution_count": null, 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [ 341 | "Ncorr3 = model3.noiseCorrelations(maxTime=20, legend_loc='upper right', legend_fontsize=12)" 342 | ] 343 | }, 344 | { 345 | "cell_type": "code", 346 | "execution_count": null, 347 | "metadata": {}, 348 | "outputs": [], 349 | "source": [ 350 | "Ncorr3.showLogs()" 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": null, 356 | "metadata": {}, 357 | "outputs": [], 358 | "source": [ 359 | "model3.showFokkerPlanckEquation()" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": null, 365 | "metadata": {}, 366 | "outputs": [], 367 | "source": [ 368 | "model3.showNoiseEquations()" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": null, 374 | "metadata": {}, 375 | "outputs": [], 376 | "source": [ 377 | "model3.showNoiseSolutions()" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": null, 383 | "metadata": {}, 384 | "outputs": [], 385 | "source": [ 386 | "stream3 = model3.stream('X', 'Y', showNoise=True, showFixedPoints=True)" 387 | ] 388 | }, 389 | { 390 | "cell_type": "code", 391 | "execution_count": null, 392 | "metadata": {}, 393 | "outputs": [], 394 | "source": [ 395 | "stream3.showLogs()" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "## Pitchfork bifurcation: a prototypical example " 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": null, 408 | "metadata": {}, 409 | "outputs": [], 410 | "source": [ 411 | "model4 = mumot.parseModel(r\"\"\"\n", 412 | "\\emptyset + X -> X + X : \\mu\n", 413 | "X + X + X -> X + X + \\emptyset : \\alpha\n", 414 | "Y -> \\emptyset : \\beta\n", 415 | "(A) -> Y : \\kappa\n", 416 | "X -> \\emptyset : \\gamma\n", 417 | "\"\"\")" 418 | ] 419 | }, 420 | { 421 | "cell_type": "code", 422 | "execution_count": null, 423 | "metadata": {}, 424 | "outputs": [], 425 | "source": [ 426 | "model4.showODEs(method='vanKampen')" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": null, 432 | "metadata": {}, 433 | "outputs": [], 434 | "source": [ 435 | "evol4 = model4.integrate(['X', 'Y'], legend_loc='center right')" 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "execution_count": null, 441 | "metadata": {}, 442 | "outputs": [], 443 | "source": [ 444 | "evol4.showLogs()" 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": null, 450 | "metadata": {}, 451 | "outputs": [], 452 | "source": [ 453 | "Ncorr4 = model4.noiseCorrelations(maxTime=20, legend_loc='upper right', legend_fontsize=12)" 454 | ] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "execution_count": null, 459 | "metadata": {}, 460 | "outputs": [], 461 | "source": [ 462 | "Ncorr4.showLogs()" 463 | ] 464 | }, 465 | { 466 | "cell_type": "code", 467 | "execution_count": null, 468 | "metadata": {}, 469 | "outputs": [], 470 | "source": [ 471 | "model4.showFokkerPlanckEquation()" 472 | ] 473 | }, 474 | { 475 | "cell_type": "code", 476 | "execution_count": null, 477 | "metadata": {}, 478 | "outputs": [], 479 | "source": [ 480 | "model4.showNoiseEquations()" 481 | ] 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": null, 486 | "metadata": {}, 487 | "outputs": [], 488 | "source": [ 489 | "model4.showNoiseSolutions()" 490 | ] 491 | }, 492 | { 493 | "cell_type": "code", 494 | "execution_count": null, 495 | "metadata": {}, 496 | "outputs": [], 497 | "source": [ 498 | "stream4 = model4.stream('X', 'Y', showNoise=True, showFixedPoints=False)" 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": null, 504 | "metadata": {}, 505 | "outputs": [], 506 | "source": [ 507 | "stream4.showLogs()" 508 | ] 509 | }, 510 | { 511 | "cell_type": "markdown", 512 | "metadata": {}, 513 | "source": [ 514 | "## Time evolution of stop-signal model" 515 | ] 516 | }, 517 | { 518 | "cell_type": "code", 519 | "execution_count": null, 520 | "metadata": {}, 521 | "outputs": [], 522 | "source": [ 523 | "model5 = mumot.parseModel(r\"\"\"\n", 524 | "U -> A : g_A\n", 525 | "U -> B : g_B\n", 526 | "A -> U : a_A\n", 527 | "B -> U : a_B\n", 528 | "A + U -> A + A : r_A\n", 529 | "B + U -> B + B : r_B\n", 530 | "A + B -> A + U : s\n", 531 | "A + B -> B + U : s\n", 532 | "\"\"\")" 533 | ] 534 | }, 535 | { 536 | "cell_type": "code", 537 | "execution_count": null, 538 | "metadata": {}, 539 | "outputs": [], 540 | "source": [ 541 | "evol5 = model5.integrate(['A', 'B', 'U'], maxTime=50, legend_loc='center right', legend_fontsize=13)" 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": null, 547 | "metadata": {}, 548 | "outputs": [], 549 | "source": [ 550 | "evol5.showLogs()" 551 | ] 552 | }, 553 | { 554 | "cell_type": "code", 555 | "execution_count": null, 556 | "metadata": {}, 557 | "outputs": [], 558 | "source": [ 559 | "Ncorr5 = model5.noiseCorrelations(maxTime=50, legend_loc='upper right', legend_fontsize=12)" 560 | ] 561 | }, 562 | { 563 | "cell_type": "code", 564 | "execution_count": null, 565 | "metadata": {}, 566 | "outputs": [], 567 | "source": [ 568 | "Ncorr5.showLogs()" 569 | ] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "execution_count": null, 574 | "metadata": {}, 575 | "outputs": [], 576 | "source": [ 577 | "model5.showNoiseEquations()" 578 | ] 579 | }, 580 | { 581 | "cell_type": "markdown", 582 | "metadata": {}, 583 | "source": [ 584 | "## Stop-signal model with 3 options" 585 | ] 586 | }, 587 | { 588 | "cell_type": "code", 589 | "execution_count": null, 590 | "metadata": {}, 591 | "outputs": [], 592 | "source": [ 593 | "model6 = mumot.parseModel(r\"\"\"\n", 594 | "U -> A : g_1\n", 595 | "U -> B : g_2\n", 596 | "U -> C : g_3\n", 597 | "A -> U : a_1\n", 598 | "B -> U : a_2\n", 599 | "C -> U : a_3\n", 600 | "A + U -> A + A : r_1\n", 601 | "B + U -> B + B : r_2\n", 602 | "C + U -> C + C : r_3\n", 603 | "A + B -> A + U : s\n", 604 | "A + B -> B + U : s\n", 605 | "A + C -> A + U : s\n", 606 | "A + C -> C + U : s\n", 607 | "B + C -> B + U : s\n", 608 | "B + C -> C + U : s\n", 609 | "\"\"\")" 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": null, 615 | "metadata": {}, 616 | "outputs": [], 617 | "source": [ 618 | "evol6 = model6.integrate(['A', 'B', 'C'], maxTime=50, legend_loc='center right')" 619 | ] 620 | }, 621 | { 622 | "cell_type": "code", 623 | "execution_count": null, 624 | "metadata": {}, 625 | "outputs": [], 626 | "source": [ 627 | "evol6.showLogs()" 628 | ] 629 | }, 630 | { 631 | "cell_type": "markdown", 632 | "metadata": {}, 633 | "source": [ 634 | "## Population control of *E. coli* cells\n", 635 | "### (see You et al. (2004), Nature 428, pp. 868-871)" 636 | ] 637 | }, 638 | { 639 | "cell_type": "code", 640 | "execution_count": null, 641 | "metadata": {}, 642 | "outputs": [], 643 | "source": [ 644 | "model7 = mumot.parseModel(r\"\"\"\n", 645 | "A -> E : k_E\n", 646 | "\\emptyset + X -> X + X : k\n", 647 | "\\emptyset + X -> X + X : v_A\n", 648 | "X + X -> X + \\emptyset : k_m\n", 649 | "E -> \\emptyset : d_E\n", 650 | "X -> A : v_A\n", 651 | "A -> \\emptyset : d_A\n", 652 | "E + X -> E + \\emptyset : d_N\n", 653 | "A + \\emptyset -> A + A : k_E\n", 654 | "\"\"\")" 655 | ] 656 | }, 657 | { 658 | "cell_type": "code", 659 | "execution_count": null, 660 | "metadata": {}, 661 | "outputs": [], 662 | "source": [ 663 | "model7.showODEs(method='vanKampen')" 664 | ] 665 | }, 666 | { 667 | "cell_type": "code", 668 | "execution_count": null, 669 | "metadata": {}, 670 | "outputs": [], 671 | "source": [ 672 | "evol7 = model7.integrate(['A', 'E', 'X'], maxTime=50, legend_loc='center right')" 673 | ] 674 | }, 675 | { 676 | "cell_type": "code", 677 | "execution_count": null, 678 | "metadata": {}, 679 | "outputs": [], 680 | "source": [ 681 | "evol7.showLogs()" 682 | ] 683 | }, 684 | { 685 | "cell_type": "code", 686 | "execution_count": null, 687 | "metadata": {}, 688 | "outputs": [], 689 | "source": [ 690 | "Ncorr7 = model7.noiseCorrelations(maxTimeDS=100, tstep=0.02, maxTime=20, \n", 691 | " legend_loc='upper right', legend_fontsize=16)" 692 | ] 693 | }, 694 | { 695 | "cell_type": "code", 696 | "execution_count": null, 697 | "metadata": {}, 698 | "outputs": [], 699 | "source": [ 700 | "Ncorr7.showLogs()" 701 | ] 702 | }, 703 | { 704 | "cell_type": "code", 705 | "execution_count": null, 706 | "metadata": {}, 707 | "outputs": [], 708 | "source": [ 709 | "model7.showFokkerPlanckEquation()" 710 | ] 711 | }, 712 | { 713 | "cell_type": "code", 714 | "execution_count": null, 715 | "metadata": {}, 716 | "outputs": [], 717 | "source": [ 718 | "model7.showNoiseEquations()" 719 | ] 720 | } 721 | ], 722 | "metadata": { 723 | "kernelspec": { 724 | "display_name": "Python 3", 725 | "language": "python", 726 | "name": "python3" 727 | }, 728 | "language_info": { 729 | "codemirror_mode": { 730 | "name": "ipython", 731 | "version": 3 732 | }, 733 | "file_extension": ".py", 734 | "mimetype": "text/x-python", 735 | "name": "python", 736 | "nbconvert_exporter": "python", 737 | "pygments_lexer": "ipython3", 738 | "version": "3.6.8" 739 | }, 740 | "toc": { 741 | "base_numbering": 1, 742 | "nav_menu": {}, 743 | "number_sections": true, 744 | "sideBar": true, 745 | "skip_h1_title": false, 746 | "title_cell": "Table of Contents", 747 | "title_sidebar": "Contents", 748 | "toc_cell": false, 749 | "toc_position": {}, 750 | "toc_section_display": true, 751 | "toc_window_display": false 752 | } 753 | }, 754 | "nbformat": 4, 755 | "nbformat_minor": 1 756 | } 757 | -------------------------------------------------------------------------------- /TestNotebooks/MiscTests/MuMoTtest_bifurcation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# This notebook provides a quick test for bifurcation plots in MuMoT." 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import mumot\n", 17 | "\n", 18 | "mumot.__version__" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "%%model\n", 28 | "$\n", 29 | "U -> A : g_A\n", 30 | "U -> B : g_B\n", 31 | "A -> U : a_A\n", 32 | "B -> U : a_B\n", 33 | "A + U -> A + A : r_A\n", 34 | "B + U -> B + B : r_B\n", 35 | "A + B -> A + U : s\n", 36 | "A + B -> B + U : s\n", 37 | "$" 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "model1 = mumot.parseModel(In[2])" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "model2 = model1.substitute('U = N - A - B')" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "model3 = model2.substitute('a_A = 1/v_A, a_B = 1/v_B, g_A = v_A, g_B = v_B, r_A = v_A, r_B = v_B')" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "model4 = model3.substitute('v_A = \\mu + \\Delta/2, v_B = \\mu - \\Delta/2')" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "bifurcation1 = model4.bifurcation('s','A-B', choose_xrange = [0,5], initWidgets={'initBifParam': [4.5,0,5,0.1]})\n", 83 | "# to get started, here is a parameter combination that works\n", 84 | "# set \\mu = 3\n", 85 | "# set \\Delta = 0.1 for unfolding of pitchfork\n", 86 | "# set \\Delta = 0 for pitchfork" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "metadata": {}, 93 | "outputs": [], 94 | "source": [ 95 | "bifurcation1.showLogs()" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "bifurcation2 = model4.bifurcation('\\\\Delta','A-B', initBifParam = 2.0, params = [('\\\\mu', 4), ('s', 4)], \n", 105 | " choose_xrange = [-2,2])" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "bifurcation2.showLogs()" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "multiController4 = mumot.MuMoTmultiController([model4.bifurcation('s','A', silent = True), \n", 124 | " model4.bifurcation('s','B', silent = True)], \n", 125 | " shareAxes = False)" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "multiController4.showLogs()" 135 | ] 136 | } 137 | ], 138 | "metadata": { 139 | "kernelspec": { 140 | "display_name": "Python 3", 141 | "language": "python", 142 | "name": "python3" 143 | }, 144 | "language_info": { 145 | "codemirror_mode": { 146 | "name": "ipython", 147 | "version": 3 148 | }, 149 | "file_extension": ".py", 150 | "mimetype": "text/x-python", 151 | "name": "python", 152 | "nbconvert_exporter": "python", 153 | "pygments_lexer": "ipython3", 154 | "version": "3.5.4" 155 | }, 156 | "toc": { 157 | "base_numbering": 1, 158 | "nav_menu": {}, 159 | "number_sections": true, 160 | "sideBar": true, 161 | "skip_h1_title": false, 162 | "title_cell": "Table of Contents", 163 | "title_sidebar": "Contents", 164 | "toc_cell": false, 165 | "toc_position": {}, 166 | "toc_section_display": true, 167 | "toc_window_display": false 168 | } 169 | }, 170 | "nbformat": 4, 171 | "nbformat_minor": 1 172 | } 173 | -------------------------------------------------------------------------------- /TestNotebooks/MiscTests/MuMoTtest_oneDimensionalModels.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# This notebook demonstrates some functionality of MuMoT when working with one-dimensional systems. " 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import mumot\n", 17 | "\n", 18 | "mumot.__version__\n", 19 | "mumot.about()" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## This model corresponds to the normal form of a pitchfork bifurcation in 1D #######" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "model1 = mumot.parseModel(r\"\"\"\n", 36 | "\\emptyset + \\alpha_\\beta -> \\alpha_\\beta + \\alpha_\\beta : r\n", 37 | "\\alpha_\\beta + \\alpha_\\beta + \\alpha_\\beta -> \\emptyset + \\emptyset + \\emptyset : b\n", 38 | "\"\"\")" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "model1.showODEs()" 48 | ] 49 | }, 50 | { 51 | "cell_type": "code", 52 | "execution_count": null, 53 | "metadata": {}, 54 | "outputs": [], 55 | "source": [ 56 | "model1.showMasterEquation()" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": null, 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "model1.showVanKampenExpansion()" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "model1.showFokkerPlanckEquation()" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "model1.showODEs(method='vanKampen')" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "model1.showODEs()" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": null, 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "model1.showNoiseEquations()" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": { 108 | "scrolled": true 109 | }, 110 | "outputs": [], 111 | "source": [ 112 | "model1.showNoiseSolutions()" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": null, 118 | "metadata": {}, 119 | "outputs": [], 120 | "source": [ 121 | "mint1 = model1.integrate(initWidgets={'maxTime': [10,1,50,1]})" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "mint1.showLogs()" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "model1.noiseCorrelations(initWidgets={'maxTime': [10,1,50,1]})" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "bif1 = model1.bifurcation('r', r'\\alpha_\\beta')" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "bif1.showLogs()" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "metadata": { 164 | "scrolled": false 165 | }, 166 | "outputs": [], 167 | "source": [ 168 | "mc1 = mumot.MuMoTmultiController([model1.integrate(params=[('b',2)], choose_yrange=[0,1], silent=True),\n", 169 | " model1.integrate(params=[('b',1)], choose_yrange=[0,1], silent=True)],\n", 170 | " shareAxes = False, silent=False);" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "mc1.showLogs()" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "mumot.MuMoTmultiController([model1.integrate(initWidgets={'maxTime':[10,5,50,1], \n", 189 | " 'initialState':{'\\\\alpha_\\\\beta': [0.3,0,1,0.01]}},\n", 190 | " conserved=False, silent=True),\n", 191 | " model1.noiseCorrelations(initWidgets={'maxTime':[10,5,50,1], \n", 192 | " 'initialState':{'\\\\alpha_\\\\beta': [0.5,0,1,0.01]}},\n", 193 | " conserved=False, silent=True)], shareAxes = False)" 194 | ] 195 | }, 196 | { 197 | "cell_type": "markdown", 198 | "metadata": {}, 199 | "source": [ 200 | "## This model is from van Kampen's book (1985, pp. 254), where analytical resultsare available for comparison (it works!)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "metadata": {}, 207 | "outputs": [], 208 | "source": [ 209 | "model2 = mumot.parseModel(r\"\"\"\n", 210 | "(A) -> X : k\n", 211 | "X + X -> \\emptyset + \\emptyset : h\n", 212 | "\"\"\")" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": null, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "model2.show()" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "model2.showODEs()" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "metadata": {}, 237 | "outputs": [], 238 | "source": [ 239 | "model2.showODEs(method='vanKampen')" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": null, 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "model2.showMasterEquation()" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": {}, 255 | "outputs": [], 256 | "source": [ 257 | "model3 = model2.substitute('h = v, k = m')" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": null, 263 | "metadata": {}, 264 | "outputs": [], 265 | "source": [ 266 | "model3.showODEs()" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": null, 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "model3.showODEs(method='vanKampen')" 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": null, 281 | "metadata": {}, 282 | "outputs": [], 283 | "source": [ 284 | "model3.showNoiseEquations()" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": null, 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "model3.showNoiseSolutions()" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "metadata": {}, 300 | "outputs": [], 301 | "source": [ 302 | "model2.integrate()" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": null, 308 | "metadata": {}, 309 | "outputs": [], 310 | "source": [ 311 | "model3.integrate()" 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": null, 317 | "metadata": {}, 318 | "outputs": [], 319 | "source": [ 320 | "model3.showStoichiometry()" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": null, 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [ 329 | "model2.showStoichiometry()" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": null, 335 | "metadata": {}, 336 | "outputs": [], 337 | "source": [ 338 | "model2.noiseCorrelations()" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": null, 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [ 347 | "model3.bifurcation('A','X')" 348 | ] 349 | } 350 | ], 351 | "metadata": { 352 | "kernelspec": { 353 | "display_name": "Python 3", 354 | "language": "python", 355 | "name": "python3" 356 | }, 357 | "language_info": { 358 | "codemirror_mode": { 359 | "name": "ipython", 360 | "version": 3 361 | }, 362 | "file_extension": ".py", 363 | "mimetype": "text/x-python", 364 | "name": "python", 365 | "nbconvert_exporter": "python", 366 | "pygments_lexer": "ipython3", 367 | "version": "3.5.4" 368 | }, 369 | "toc": { 370 | "nav_menu": {}, 371 | "number_sections": true, 372 | "sideBar": true, 373 | "skip_h1_title": false, 374 | "title_cell": "Table of Contents", 375 | "title_sidebar": "Contents", 376 | "toc_cell": false, 377 | "toc_position": {}, 378 | "toc_section_display": true, 379 | "toc_window_display": false 380 | } 381 | }, 382 | "nbformat": 4, 383 | "nbformat_minor": 2 384 | } 385 | -------------------------------------------------------------------------------- /apt.txt: -------------------------------------------------------------------------------- 1 | texlive-latex-base 2 | texlive-latex-recommended 3 | texlive-science 4 | texlive-latex-extra 5 | texlive-fonts-recommended 6 | dvipng 7 | ghostscript 8 | graphviz 9 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = -v 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = MuMoT 8 | SOURCEDIR = source 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/MuMoTpaperResults.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Results for *Multiscale Modelling Tool: Mathematical Modelling of Collective Behaviour without the Maths*, Marshall, Reina and Bose " 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import mumot\n", 17 | "\n", 18 | "mumot.about()" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## Honeybee stop-signal model (Seeley *et al.* (2012) *Science* **335**, 108-111)" 26 | ] 27 | }, 28 | { 29 | "cell_type": "code", 30 | "execution_count": null, 31 | "metadata": {}, 32 | "outputs": [], 33 | "source": [ 34 | "%%model\n", 35 | "$\n", 36 | "U -> A : g_A\n", 37 | "U -> B : g_B\n", 38 | "A -> U : a_A\n", 39 | "B -> U : a_B\n", 40 | "A + U -> A + A : r_A\n", 41 | "B + U -> B + B : r_B\n", 42 | "A + B -> A + U : s\n", 43 | "A + B -> B + U : s\n", 44 | "$" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "model1 = mumot.parseModel(In[2])" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "model1.show()" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "model2 = model1.substitute('U = N - A - B')" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "metadata": {}, 78 | "outputs": [], 79 | "source": [ 80 | "model3 = model2.substitute('a_A = 1/v_A, a_B = 1/v_B, g_A = v_A, g_B = v_B, r_A = v_A, r_B = v_B')" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": null, 86 | "metadata": {}, 87 | "outputs": [], 88 | "source": [ 89 | "model4 = model3.substitute('v_A = \\mu + \\Delta/2, v_B = \\mu - \\Delta/2')" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "controller1 = model4.integrate()" 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "execution_count": null, 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "bees = model4" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "stream1 = bees.stream('A', 'B', showFixedPoints = True, showNoise = True)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": { 123 | "scrolled": false 124 | }, 125 | "outputs": [], 126 | "source": [ 127 | "bifurcation1 = bees.bifurcation('s','A-B', \n", 128 | " initWidgets={'mu':[3, 1, 5, 0.5], 'Delta':[0, 0, 2, 0.1], \n", 129 | " 'initBifParam':[4.5, 4, 6, 0.1]},\n", 130 | " choose_xrange=[0, 5])" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": { 137 | "scrolled": false 138 | }, 139 | "outputs": [], 140 | "source": [ 141 | "bifurcation2 = bees.bifurcation('\\\\Delta','A-B', \n", 142 | " initWidgets={'mu':[3, 1, 5, 0.5], 's':[5, 4, 6, 0.5]},\n", 143 | " choose_xrange=[-1,1])" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "metadata": {}, 150 | "outputs": [], 151 | "source": [ 152 | "ssa1 = bees.SSA()" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": null, 158 | "metadata": {}, 159 | "outputs": [], 160 | "source": [ 161 | "multiagent1 = bees.multiagent()" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "bees.showMasterEquation()" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "#bees.showVanKampenExpansion()" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "#bees.showFokkerPlanckEquation()" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "## Lotka-Volterra dynamics (see J.D. Murray (1991), *Mathematical Biology I: An Introduction (Third Edition)*, pp. 79-81))" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "%%model\n", 205 | "$\n", 206 | "(A) + X -> X + X : \\alpha\n", 207 | "X + Y -> Y + Y : \\beta\n", 208 | "Y -> \\emptyset : \\gamma\n", 209 | "$" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "lv = mumot.parseModel(In[-2])" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": {}, 225 | "outputs": [], 226 | "source": [ 227 | "stream2 = lv.stream('X', 'Y', showFixedPoints = True)" 228 | ] 229 | }, 230 | { 231 | "cell_type": "markdown", 232 | "metadata": {}, 233 | "source": [ 234 | "## Brusellator (Prigogine and Lefevre (1968); see J.D. Murray (1991), *Mathematical Biology I: An Introduction (Third Edition)*, p. 253))" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": null, 240 | "metadata": {}, 241 | "outputs": [], 242 | "source": [ 243 | "%%model\n", 244 | "$\n", 245 | "(\\alpha) -> X : \\gamma\n", 246 | "X + X + Y -> X + X + X : \\chi\n", 247 | "(\\beta) + X -> Y + \\emptyset : \\delta\n", 248 | "X -> \\emptyset : \\xi\n", 249 | "$" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [ 258 | "brusselator = mumot.parseModel(In[-2])" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": null, 264 | "metadata": {}, 265 | "outputs": [], 266 | "source": [ 267 | "integrate1 = brusselator.integrate()" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "metadata": {}, 274 | "outputs": [], 275 | "source": [ 276 | "stream3 = brusselator.stream('X','Y', showFixedPoints = True, showNoise = True)" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": {}, 282 | "source": [ 283 | "## van Kampen expansion; see N. G. van Kampen (1985) *Stochastic Processes in Physics and Chemistry (Third Edition)*, pp. 244-246)" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "metadata": {}, 290 | "outputs": [], 291 | "source": [ 292 | "%%model\n", 293 | "$\n", 294 | "(A) -> X : k\n", 295 | "X + X -> \\emptyset + \\emptyset : h\n", 296 | "$" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": null, 302 | "metadata": {}, 303 | "outputs": [], 304 | "source": [ 305 | "vk = mumot.parseModel(In[-2])" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "vk.show()" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": null, 320 | "metadata": {}, 321 | "outputs": [], 322 | "source": [ 323 | "vk.showODEs(method='vanKampen')" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": null, 329 | "metadata": {}, 330 | "outputs": [], 331 | "source": [ 332 | "vk.showMasterEquation()" 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "execution_count": null, 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [ 341 | "vk.showFokkerPlanckEquation()" 342 | ] 343 | } 344 | ], 345 | "metadata": { 346 | "kernelspec": { 347 | "display_name": "Python 3", 348 | "language": "python", 349 | "name": "python3" 350 | }, 351 | "language_info": { 352 | "codemirror_mode": { 353 | "name": "ipython", 354 | "version": 3 355 | }, 356 | "file_extension": ".py", 357 | "mimetype": "text/x-python", 358 | "name": "python", 359 | "nbconvert_exporter": "python", 360 | "pygments_lexer": "ipython3", 361 | "version": "3.5.4" 362 | }, 363 | "toc": { 364 | "base_numbering": 1, 365 | "nav_menu": {}, 366 | "number_sections": true, 367 | "sideBar": true, 368 | "skip_h1_title": false, 369 | "title_cell": "Table of Contents", 370 | "title_sidebar": "Contents", 371 | "toc_cell": false, 372 | "toc_position": {}, 373 | "toc_section_display": true, 374 | "toc_window_display": true 375 | } 376 | }, 377 | "nbformat": 4, 378 | "nbformat_minor": 2 379 | } 380 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=MuMoT 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/source/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiODeProject/MuMoT/cb273bece99f5fddae8bc67780f41d1a29737f2c/docs/source/_static/.gitkeep -------------------------------------------------------------------------------- /docs/source/_static/branch_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiODeProject/MuMoT/cb273bece99f5fddae8bc67780f41d1a29737f2c/docs/source/_static/branch_model.png -------------------------------------------------------------------------------- /docs/source/about.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | Contributors 5 | ------------ 6 | 7 | Core Development Team: 8 | 9 | * `James A. R. Marshall`_ (james.marshall@sheffield.ac.uk) 10 | * `Andreagiovanni Reina`_ 11 | * `Thomas Bose`_ 12 | 13 | Contributors: 14 | 15 | * Robert Dennison 16 | 17 | Packaging, Documentation and Deployment: 18 | 19 | * `Will Furnass`_ 20 | 21 | Windows Compatibility: 22 | 23 | * `Renato Pagliara Vasquez`_ 24 | 25 | Citation 26 | -------- 27 | 28 | If you use MuMoT in your own work please cite the original paper describing MuMoT, as well as the DOI for the version of MuMoT you used (to credit developers involved in that version): 29 | 30 | * Marshall, J. A. R., Reina, A., Bose, T. (2019) Multiscale Modelling Tool: Mathematical modelling of collective behaviour without the maths. PLoS one **14**(9): e0222906. `doi:10.1371/journal.pone.0222906 `__ 31 | * Marshall, J. A. R., Reina, A., Bose, T., Dennison, R., Furnass, W., Pagliara Vasquez, R. (2020) MuMoT release 1.2.2 `10.15131/shef.data.9925010.v2 `__ 32 | * Marshall, J. A. R., Reina, A., Bose, T., Dennison, R., Furnass, W., Pagliara Vasquez, R. (2019) MuMoT release 1.1.2 `doi:10.15131/shef.data.9925010.v1 `__ 33 | * Marshall, J. A. R., Reina, A., Bose, T. (2019) MuMoT release 1.0.0 `doi:10.15131/shef.data.7951298.v1 `__ 34 | 35 | .. todo:: Add formatted citation and BibTeX entry inc ORDA_ (FigShare) / Zenodo_ DOI. 36 | 37 | Acknowledgments 38 | --------------- 39 | 40 | Funding 41 | ^^^^^^^ 42 | 43 | MuMoT was developed with funds from the `European Research Council`_ (ERC) 44 | under the European Union's `Horizon 2020`_ research and innovation programme 45 | (grant agreement number 647704 - DiODe_), and from Corporate Information 46 | and Computing Services, University of Sheffield. 47 | 48 | Included third-party code 49 | ^^^^^^^^^^^^^^^^^^^^^^^^^ 50 | 51 | * ``mumot/__init__.py``: contains functions (C) 2012 Free Software Foundation, under the MIT Licence 52 | 53 | mybinder.org 54 | ^^^^^^^^^^^^ 55 | 56 | The MuMoT manual is :ref:`runnable online ` via the the excellent and free `mybinder.org `__ service, 57 | funded by the `Gordon and Betty Moore Foundation `__. 58 | 59 | .. _Andreagiovanni Reina: https://areina.staff.shef.ac.uk/ 60 | .. _DiODe: http://diode.group.shef.ac.uk/ 61 | .. _European Research Council: lhttps://erc.europa.eu/>`__ 62 | .. _Horizon 2020: https://ec.europa.eu/programmes/horizon2020/en/ 63 | .. _James A. R. Marshall: https://staffwww.dcs.shef.ac.uk/people/J.Marshall/james.html 64 | .. _ORDA: https://orda.shef.ac.uk/ 65 | .. _Renato Pagliara Vasquez: https://mae.princeton.edu/people/graduate-students/vasquez 66 | .. _Thomas Bose: http://thomas-bose.staff.shef.ac.uk/ 67 | .. _Will Furnass: https://learningpatterns.me/ 68 | .. _Zenodo: https://zenodo.org/ 69 | -------------------------------------------------------------------------------- /docs/source/api.rst: -------------------------------------------------------------------------------- 1 | API 2 | === 3 | 4 | .. todo:: Add preamble before API documentation. Note that primarily of interest to developers. 5 | 6 | .. currentmodule:: mumot 7 | 8 | 9 | Functions 10 | --------- 11 | 12 | .. autosummary:: 13 | :toctree: autosummary 14 | 15 | about 16 | parseModel 17 | setVerboseExceptions 18 | 19 | Model classes 20 | ------------- 21 | 22 | .. autosummary:: 23 | :toctree: autosummary 24 | 25 | MuMoTmodel 26 | 27 | View classes 28 | ------------ 29 | 30 | .. inheritance-diagram:: mumot.views 31 | :parts: 1 32 | 33 | .. autosummary:: 34 | :toctree: autosummary 35 | 36 | MuMoTview 37 | MuMoTmultiView 38 | MuMoTtimeEvolutionView 39 | MuMoTintegrateView 40 | MuMoTnoiseCorrelationsView 41 | MuMoTfieldView 42 | MuMoTvectorView 43 | MuMoTstreamView 44 | MuMoTbifurcationView 45 | MuMoTstochasticSimulationView 46 | MuMoTmultiagentView 47 | MuMoTSSAView 48 | 49 | Controller classes 50 | ------------------ 51 | 52 | .. inheritance-diagram:: mumot.controllers 53 | :parts: 1 54 | 55 | .. autosummary:: 56 | :toctree: autosummary 57 | 58 | MuMoTcontroller 59 | MuMoTbifurcationController 60 | MuMoTtimeEvolutionController 61 | MuMoTstochasticSimulationController 62 | MuMoTmultiagentController 63 | MuMoTmultiController 64 | 65 | Exception classes 66 | ----------------- 67 | 68 | .. inheritance-diagram:: mumot.exceptions 69 | :parts: 1 70 | 71 | .. autosummary:: 72 | :toctree: autosummary 73 | 74 | MuMoTError 75 | MuMoTValueError 76 | MuMoTSyntaxError 77 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # MuMoT documentation build configuration file, created by 5 | # sphinx-quickstart on Thu Dec 14 17:49:51 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # -- General configuration ------------------------------------------------ 17 | 18 | # If your documentation needs a minimal Sphinx version, state it here. 19 | # 20 | # needs_sphinx = '1.0' 21 | 22 | # Add any Sphinx extension module names here, as strings. They can be 23 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 24 | # ones. 25 | extensions = [ 26 | 'sphinx.ext.autodoc', 27 | 'sphinx.ext.autosummary', 28 | 'sphinx.ext.doctest', 29 | 'sphinx.ext.todo', 30 | 'sphinx.ext.coverage', 31 | 'sphinx.ext.extlinks', 32 | 'sphinx.ext.mathjax', 33 | 'sphinx.ext.napoleon', 34 | 'sphinx.ext.viewcode', 35 | 'sphinx.ext.githubpages', 36 | 'sphinx.ext.inheritance_diagram' 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # The suffix(es) of source filenames. 43 | # You can specify multiple suffix as a list of string: 44 | # 45 | # source_suffix = ['.rst', '.md'] 46 | source_suffix = '.rst' 47 | 48 | # The master toctree document. 49 | master_doc = 'index' 50 | 51 | # General information about the project. 52 | project = 'MuMoT' 53 | copyright = '2020, University of Sheffield' 54 | author = 'James A. R. Marshall, Andreagiovanni Reina, Thomas Bose' 55 | 56 | # The version info for the project you're documenting, acts as replacement for 57 | # |version| and |release|, also used in various other places throughout the 58 | # built documents. 59 | #import mumot 60 | from pkg_resources import get_distribution 61 | release = get_distribution('mumot').version 62 | version = '.'.join(release.split('.')[:3]) 63 | 64 | #binder_tag = f"v{version}" if release == version else "master" 65 | #extlinks = {'binder': (f"https://mybinder.org/v2/gh/DiODeProject/MuMoT/{binder_tag}?filepath=%s.ipynb", "issue ")} 66 | 67 | # The language for content autogenerated by Sphinx. Refer to documentation 68 | # for a list of supported languages. 69 | # 70 | # This is also used if you do content translation via gettext catalogs. 71 | # Usually you set "language" from the command line for these cases. 72 | language = None 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | # This patterns also effect to html_static_path and html_extra_path 77 | exclude_patterns = [] 78 | 79 | # The name of the Pygments (syntax highlighting) style to use. 80 | pygments_style = 'sphinx' 81 | 82 | # If true, `todo` and `todoList` produce output, else they produce nothing. 83 | todo_include_todos = False 84 | 85 | 86 | # -- Options for HTML output ---------------------------------------------- 87 | 88 | # The theme to use for HTML and HTML Help pages. See the documentation for 89 | # a list of builtin themes. 90 | # 91 | html_theme = "nature" 92 | 93 | # Theme options are theme-specific and customize the look and feel of a theme 94 | # further. For a list of options available for each theme, see the 95 | # documentation. 96 | # 97 | # html_theme_options = {} 98 | 99 | # Add any paths that contain custom static files (such as style sheets) here, 100 | # relative to this directory. They are copied after the builtin static files, 101 | # so a file named "default.css" will overwrite the builtin "default.css". 102 | html_static_path = ['_static'] 103 | 104 | # Custom sidebar templates, must be a dictionary that maps document names 105 | # to template names. 106 | # 107 | html_sidebars = { '**': ['globaltoc.html', 'searchbox.html'] } 108 | 109 | # -- Options for HTMLHelp output ------------------------------------------ 110 | 111 | # Output file base name for HTML help builder. 112 | htmlhelp_basename = 'MuMoTdoc' 113 | 114 | 115 | # -- Options for LaTeX output --------------------------------------------- 116 | 117 | latex_elements = { 118 | # The paper size ('letterpaper' or 'a4paper'). 119 | # 120 | # 'papersize': 'letterpaper', 121 | 122 | # The font size ('10pt', '11pt' or '12pt'). 123 | # 124 | # 'pointsize': '10pt', 125 | 126 | # Additional stuff for the LaTeX preamble. 127 | # 128 | # 'preamble': '', 129 | 130 | # Latex figure (float) alignment 131 | # 132 | # 'figure_align': 'htbp', 133 | } 134 | 135 | # Grouping the document tree into LaTeX files. List of tuples 136 | # (source start file, target name, title, 137 | # author, documentclass [howto, manual, or own class]). 138 | latex_documents = [ 139 | (master_doc, 'MuMoT.tex', 'MuMoT Documentation', 140 | 'James A. R. Marshall, Andreagiovanni Reina, Thomas Bose\'', 'manual'), 141 | ] 142 | 143 | 144 | # -- Options for manual page output --------------------------------------- 145 | 146 | # One entry per manual page. List of tuples 147 | # (source start file, name, description, authors, manual section). 148 | man_pages = [ 149 | (master_doc, 'mumot', 'MuMoT Documentation', 150 | [author], 1) 151 | ] 152 | 153 | 154 | # -- Options for Texinfo output ------------------------------------------- 155 | 156 | # Grouping the document tree into Texinfo files. List of tuples 157 | # (source start file, target name, title, author, 158 | # dir menu entry, description, category) 159 | texinfo_documents = [ 160 | (master_doc, 'MuMoT', 'MuMoT Documentation', 161 | author, 'MuMoT', 'One line description of project.', 162 | 'Miscellaneous'), 163 | ] 164 | 165 | # -- Options for autodoc and autosummary ---------------------------------- 166 | 167 | autodoc_default_options = { 168 | # Make sure that any autodoc declarations show the right members 169 | "members": None, 170 | "inherited-members": None, 171 | "show-inheritance": None, 172 | # "private-members": None, 173 | } 174 | autosummary_generate = True 175 | 176 | # -- Options for napoleon/numpydoc ---------------------------------------- 177 | napoleon_numpy_docstring = True 178 | napoleon_google_docstring = False 179 | napoleon_use_param = False 180 | napoleon_use_rtype = False 181 | -------------------------------------------------------------------------------- /docs/source/development.rst: -------------------------------------------------------------------------------- 1 | Development 2 | =========== 3 | 4 | .. contents:: :local: 5 | 6 | Reporting issues 7 | ---------------- 8 | 9 | If your issue (feature request or bug) has not already been filed in the MuMoT GitHub repository 10 | (`list of all open issues `__) 11 | then please `file a new Issue `__ 12 | against the `MuMoT GitHub repository`_. 13 | 14 | .. _cont_wflow: 15 | 16 | Contribution workflow 17 | --------------------- 18 | 19 | If you want to contribute a feature or fix a bug then: 20 | 21 | #. `Fork `__ the `MuMoT GitHub repository`_ 22 | so you have your own personal MuMoT repository on GitHub. 23 | #. Create a `feature branch `__ 24 | off the master branch within your personal MuMoT repository. 25 | See the *branch and versioning policy* below for more information about how branches, tags and versions are managed in this project. 26 | #. Make commits to that branch: 27 | 28 | * Style: write code using `standard Python naming conventions `_. 29 | * Testing: if you add new features, fix bug(s) or change existing functionality: 30 | 31 | * Add (lower-level) `unit tests `__ to 32 | the Python source files in the ``tests/`` directory. 33 | * Add (higher-level) `acceptance `__/`regression `__ tests 34 | to ``TestNotebooks/MuMoTtest.ipynb`` (or to/as Notebooks in the ``TestNotebooks/MiscTests/`` directory). 35 | 36 | * Documentation: include Python docstrings documentation in the numpydoc_ format for all modules, functions, classes, methods and (if applicable) attributes. 37 | * Do not commit an updated User Manual Notebook or test notebooks containing output cells; all output cells should be stripped first using: 38 | 39 | .. code:: sh 40 | 41 | jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace docs/MuMoTuserManual.ipynb 42 | 43 | Note that the test suite checks this automatically. 44 | 45 | * Use comments containing ``@todo`` in Python code for reminders. 46 | 47 | #. When you are ready to merge your feature branch into the ``master`` branch of the 'upstream' repository: 48 | 49 | #. Run all tests using ``tox`` (see Testing_) first. 50 | #. Create a `Pull Request`_ to request that 51 | your feature branch be merged into the master branch of the upstream repository. 52 | Automated tests will then run against your branch (using `GitHub Actions `) 53 | and your code will be reviewed by the core development team, 54 | after which your changes will be merged into the main MuMoT repository *or* 55 | you will be asked to make further changes to your branch. 56 | 57 | .. _testing: 58 | 59 | Setting up a local development environment 60 | ------------------------------------------ 61 | 62 | Follow the :ref:`install instructions ` but ensure you run: 63 | 64 | .. code:: sh 65 | 66 | python3 -m pip install path/to/clone/of/MuMoT/repository[test,docs] 67 | 68 | instead of just ``python3 -m pip install path/to/clone/of/MuMoT/repository``. 69 | The '``[test,docs]``' bit ensures that the optional dependencies required to run tests and build the documentation are installed. 70 | 71 | If you make local changes to the MuMoT Python package and want to use the updated package you should: 72 | 73 | #. Re-install the package within your conda environment or virtualenv (without upgrading MuMoT's dependencies): 74 | 75 | .. code:: sh 76 | 77 | python3 -m pip install --upgrade --upgrade-strategy only-if-needed path/to/clone/of/MuMoT/repository[test,docs] 78 | 79 | #. Restart any running IPython kernels within which you have imported the MuMoT package. 80 | 81 | Testing 82 | ------- 83 | 84 | .. _test_suite: 85 | 86 | Test suite 87 | ^^^^^^^^^^ 88 | 89 | Testing of MuMoT is currently very basic; 90 | the test suite only checks that certain Jupyter Notebooks run without failing i.e. there are no checks for correctness of results. 91 | However, there is a framework in place to allow more tests to be written: 92 | 93 | * **Unit tests**: run by pointing pytest_ at the ``tests/`` directory; also generates a code coverage data using pytest-cov_; *only a few tests implemented so far*. 94 | * **Basic integration tests**: 95 | Ensure that certain Jupyter Notebooks can be run without 96 | raising Python exceptions/errors: 97 | 98 | * ``TestNotebooks/MuMoTtest.ipynb`` 99 | * ``docs/MuMoTuserManual.ipynb`` 100 | 101 | Plus several other Notebooks (see ``tox.ini``). 102 | 103 | These tests are performed by running the Notebooks using the nbval_ plug-in for pytest_, with nbval_ being run in *lax* mode. 104 | Code coverage data is also captured at this stage when running ``TestNotebooks/MuMoTtest.ipynb`` and 105 | appended to that captured during the unit testing. 106 | * **Regression tests**: *not yet implemented*. 107 | However could be performed by running the Notebook using the nbval_ plug-in for pytest_, 108 | with nbval_ being run in normal (not *lax*) mode, 109 | to ensure that the ``TestNotebooks/MuMoTtest.ipynb`` integration test Notebook 110 | generates sufficiently similar output cells to those saved in that file 111 | when re-run in a clean environment; 112 | * **Notebook formatting/content**: 113 | Check that the User Manual Notebook does not contain output cells (as they could confuse new users). 114 | * **Documentation**: Check that Sphinx_ can build HTML documentation for the package 115 | (more info in `Building and Serving Documentation`_ section). 116 | 117 | .. _test_local: 118 | 119 | Local testing 120 | ^^^^^^^^^^^^^ 121 | 122 | To locally run the MuMoT test suite in an isolated Python environment 123 | (containing just the necessary dependencies): 124 | 125 | #. Install the tox_ testing automation tool. 126 | #. Run: 127 | 128 | .. code:: sh 129 | 130 | cd path/to/clone/of/MuMoT/repository 131 | tox 132 | 133 | This parses the ``tox.ini`` file then: 134 | 135 | #. Creates a new virtualenv_ (Python virtual environment) containing just 136 | 137 | * MuMoT's dependencies (see ``install_requires`` in ``setup.py``) 138 | * the packages needed for testing and building the documentation (see ``extras_require`` in ``setup.py``) 139 | 140 | This environment is hidden in a ``.tox`` directory to discourage developers from manually tweaking it. 141 | #. Runs the :ref:`test suite described above`. 142 | If nbval_ encounters any failures/errors then 143 | a Jupyter tab is opened in the default web browser showing 144 | the location of the failure/error. 145 | 146 | Note: attempts to measure code coverage using a Notebook will fail if 147 | you call the ``parseModel`` function in a Notebook by passing it a reference to 148 | an input cell that uses the ``%%model`` cell magic; you need to instead 149 | call ``parseModel`` by passing it a model defined as a simple string 150 | (e.g. as is done in ``TestNotebooks/MuMoTtest.ipynb``). 151 | 152 | .. _test_ci: 153 | 154 | Automated testing using a GitHub Actions workflow 155 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 156 | 157 | Each `Pull Request`_ against the `MuMoT GitHub repository`_ and 158 | each push to the ``master`` branch in that repository 159 | trigger a `GitHub Actions ` continuous integration and continuous delivery (CI/CD) workflow. 160 | 161 | Each invocation of the workflow 162 | runs a set of user-defined tasks in an isolated execution environment, 163 | logs output from those tasks, 164 | quits early if an error is encountered 165 | and reports the exit status on completion of the job. 166 | 167 | Benefits: 168 | 169 | * Tests are run automatically without needing to be manually triggered and the results inspected by developers; 170 | * If pull requests are made from :ref:`feature branches ` against the ``master`` branch 171 | then you will be notified that tests fail *before* you merge any changes into ``master``. 172 | * You can concentrate on other things whilst the CI/CD service is running tests on your behalf. 173 | * Packages can be automatically be pushed to the Python Package Index (PyPI) if certain conditions are met (the CD part of CI/CD). 174 | 175 | The **GitHub Actions CI configuration** is in the file ``.github/workflows/test-and-release.yml``. 176 | In short, this: 177 | 178 | * :ref:`Calls tox ` to run tests for all supported Python versions; 179 | * Uploads code coverage; 180 | * Checks that source and binary *distributions* can be built for the package; 181 | * If the workflow was triggered by a tagged push to master then upload those distributions to PyPI. 182 | 183 | The GitHub Actions **dashboard** for the project shows **job exit statuses** and **logs**: 184 | `https://github.com/DiODeProject/MuMoT/actions `. 185 | From the dashboard you can restart one or more workflow jobs via *Re-run jobs*, which might be useful if 186 | a GH Actions workflow job `times out after 6h `, 187 | a entire GH Actions workflow `times out after 72h `, 188 | a job fails as it has not produced any output for several minutes 189 | or you suspect that job failures are otherwise non-deterministic. 190 | 191 | .. _build_docs: 192 | 193 | Building and serving documentation 194 | ---------------------------------- 195 | 196 | This MuMoT documentation is built using the Sphinx_ tool using/from: 197 | 198 | * The ``docs/source/conf.py`` Sphinx config file; 199 | * A number of anthropogenic pages written in reStructuredText_ format (see ``docs/source/*.rst``); 200 | * A number of pages of API documentation that were auto-generated from module/class/method/function docstrings in the MuMoT source code. 201 | (These docstrings need to be written in the numpydoc_ format and are extracted/processed by the autodoc_ and autosummary_ Sphinx extensions). 202 | 203 | The Sphinx documentation is / can be built under several different circumstances: 204 | 205 | * Manually in a development environment; 206 | * Automatically whenever :ref:`tox is run `; 207 | * Automatically whenever :ref:`a CI job is run `; 208 | * Automatically following a push to the master branch of the MuMoT repository, 209 | which causes the `ReadTheDocs `__ service to 210 | rebuild and publish the documentation at `https://mumot.readthedocs.io `__. 211 | 212 | Building the docs locally 213 | ^^^^^^^^^^^^^^^^^^^^^^^^^ 214 | 215 | #. Ensure the optional ``docs`` dependencies of ``mumot`` have been installed within your local development environment 216 | (a conda environment or virtualenv; see also the :ref:`MumoT install guide `: 217 | 218 | .. code:: 219 | 220 | python3 -m pip install path/to/clone/of/MuMoT/repository[docs] 221 | 222 | #. Move into the ``docs`` subdirectory within your MuMoT git repository: 223 | 224 | .. code:: 225 | 226 | cd path/to/clone/of/MuMoT/repository 227 | cd docs 228 | 229 | #. Install Sphinx: 230 | 231 | .. code:: 232 | 233 | python3 -m pip install sphinx 234 | 235 | #. Use Sphinx to build HTML documentation: 236 | 237 | .. code:: 238 | 239 | make html 240 | 241 | This writes output to the ``_build/html`` directory, which is ignored by git. 242 | 243 | #. (Optional) view the generated documentation: 244 | 245 | .. code:: 246 | 247 | firefox _build/html/index.html 248 | 249 | Running the User Manual Notebook on mybinder.org 250 | ------------------------------------------------ 251 | 252 | The User Manual Notebook can be run online without the need for any local installation and configuration. 253 | 254 | This is facilitated by mybinder.org_, a public instance of the BinderHub_ service. 255 | BinderHub is allows many users to start *Binder* sessions: 256 | within a session, BinderHub creates a per-session software environment on demand on remote hardware (using repo2docker_) then 257 | starts a Jupyter service within that environment. 258 | 259 | As an end user, all you need to start a BinderHub session is 260 | 261 | * The URL of an accessible Git repository that contains a software environment definition 262 | (e.g. a Python ``requirements.txt`` file, Conda ``environment.yml`` or a Docker ``Dockerfile``); 263 | * The branch, tag or commit that you'd like to access within that repository; 264 | * (Optional) a relative path within that directory to a Notebook you'd like to run. 265 | 266 | These parameters can be supplied via a web form or as URL parameters (allowing someone to just follow a link to start a Binder session). 267 | 268 | Configuration 269 | ^^^^^^^^^^^^^ 270 | 271 | Behind the scenes mybinder.org uses repo2docker to 272 | build an Ubuntu Docker image for running the MuMoT User Manual Notebook in, 273 | and pushes this to its Docker image registry. The build process has three steps: 274 | 275 | #. Install several Ubuntu packages (inc. GraphViz and a LaTeX distribution); see the ``apt.txt`` file in this repo; 276 | #. Create a Python virtualenv containing just the MuMoT Python package and its dependencies; 277 | #. Perform some post-install steps (install the TOC2 (table of contents) Jupyter extension and generate the Matplotlib font cache); see the ``postBuild`` file in this repo; 278 | 279 | After an image has been created and pushed to the image registry it remains cached there until: 280 | 281 | * a timeout is reached or; 282 | * a user requests an image for a commit for which an image has not yet been cached 283 | (e.g. if the user wants to work with the tip of master and 284 | new commits have recently been pushed to that repository. 285 | 286 | The repo2docker build process takes ~15 minutes for MuMoT; 287 | therefore note that any pushes to the master branch will invalidate any cached image for the tip of the master branch, 288 | which will increase mybinder.org startup times from seconds to ~15 minutes. 289 | 290 | **Button**: A mybinder.org session for the User Manual as of the latest stable release of MuMoT can be started by 291 | following the link in the instructions for :ref:`getting started online `. 292 | 293 | Branch, tag and version policy, inc. how to create a new release 294 | ---------------------------------------------------------------- 295 | 296 | The project uses `semantic versioning`_ e.g. compared to version ``0.8.0``: 297 | 298 | - ``0.8.1`` is a *patch* version increase - backwards-compatible bugfixes *only* 299 | - ``0.9.0`` is *minor* version increase - new functionality added in backwards-compatible manner 300 | - ``1.0.0`` is a *major* version increase - introduces incompatible API changes 301 | 302 | In this project the use of branches and git tags is as follows: 303 | 304 | - The ``master`` branch is the only long-lived *active* branch 305 | - New features are developed by creating **feature branches** from the ``master`` branch; 306 | these feature branches are then ultimately merged back into ``master`` via Pull Requests then deleted. 307 | - Changes in patch, major and minor versions are defined **solely** by 308 | creating an `annotated tag `__ 309 | for a particular commit. 310 | The name of this tag should be of the form ``v..`` 311 | i.e the version preceded by a ``v``. 312 | **The version does not need to then be specified anywhere in the code 313 | (other than in links to mybinder in the Sphinx docs)**: 314 | whenever an installable release of MuMoT is created 315 | the `setuptools_scm `__ package 316 | will embed version information using the most recent tag on the current branch 317 | plus extra information derived from the output of ``git describe`` 318 | if the most recent commit does not have an annotated tag associated with it. 319 | 320 | To create a release: 321 | 322 | #. Decide on the type of the next release (patch, major or minor), 323 | which depends on the nature of the changes. 324 | 325 | #. (Related) determine the appropriate version number for this pending release. 326 | #. Create a draft item for this release in ORDA_ (The University of Sheffield's Research Data Catalogue and Repository), 327 | so as to reserve a DOI for it. 328 | 329 | #. *Major/minor release only*: 330 | ensure all GitHub Issues tagged with the pending release (*Milestone*) 331 | have either been addressed or 332 | are reassigned to a different Milestone. 333 | Ensure all pull requests against ``master`` relating to the pending Milestone have been merged and all CI tests pass. 334 | 335 | #. If necessary, create a pull request against ``master`` to change the version in links to mybinder.org e.g. in 336 | 337 | .. code-block:: 338 | 339 | https://mybinder.org/v2/gh/DiODeProject/MuMoT/VERSION?filepath=docs%2FMuMoTuserManual.ipynb 340 | 341 | ensure ``VERSION`` is ``master`` or 342 | a particular current or future tagged version, preceded by a ``v`` e.g. ``v0.9.0``. 343 | 344 | Also, check/update citation info (including the DOI and contributors) for this pending release 345 | in ``docs/source/about.rst``. 346 | 347 | Also, update the file ``CHANGELOG.md`` with changes since the last release. 348 | You can derive this list of changes from commits made since the last release; 349 | if the last release was tagged in git with ``v0.8.0`` 350 | then you can see the first line of all commit comments since then with: :: 351 | 352 | $ git checkout master 353 | $ git log --pretty=oneline --abbrev-commit v0.8.0..HEAD 354 | 355 | then: :: 356 | 357 | $ git commit -a -m "Preparing for release of version 0.9.0" 358 | 359 | where 0.9.0 is the version of the new release. 360 | Next, create the Pull Request. 361 | 362 | #. Merge this Pull Request into ``master`` then create an *annotated tag*: :: 363 | 364 | $ git checkout master 365 | $ git fetch --prune --all 366 | $ git merge --ff-only upstream/master 367 | $ git tag -a v0.9.0 -m "Release 0.9.0" 368 | $ git push upstream --tags 369 | $ git push 370 | 371 | Here we assume that you've set up your local git repository with a remote called ``upstream`` 372 | that points at ``github.com/DiODeProject/MuMoT.git`` e.g. :: 373 | 374 | $ git remote -v 375 | origin git@github.com:willfurnass/MuMoT.git (fetch) 376 | origin git@github.com:willfurnass/MuMoT.git (push) 377 | upstream git@github.com:DiODeProject/MuMoT.git (fetch) 378 | upstream git@github.com:DiODeProject/MuMoT.git (push) 379 | 380 | NB annotated tags are are often used within git repositories to identify 381 | the commit corresponding to a particular release. 382 | 383 | #. The pushing of a tagged commit to ``github.com:DiODeProject/MuMoT.git`` causes GitHub Actions to: 384 | 385 | #. Run through the standard tasks performed for Pull Requests (see ``.github/workflows/test-and-release.yml``) *then* 386 | #. Build several *distributions* for this release of MuMoT 387 | 388 | * One or more binary 'wheel' packages e.g. ``mumot-0.9.0-py3-none-any.whl`` 389 | * A source package e.g. ``mumot-0.9.0.tar.gz`` 390 | 391 | #. Upload these files to `PyPI `__ 392 | using environment variables stored as encrypted credentials in this GitHub repo. 393 | 394 | #. You can monitor the progress of building packages for MuMoT and uploading them to PyPI 395 | using the `GitHub Actions dashboard `__. 396 | 397 | #. Attach an archive of the code/docs for this release to the draft item in ORDA. 398 | Create this archive using: :: 399 | 400 | git archive VERSION | gzip > mumot-VERSION.tar.gz 401 | 402 | For example: :: 403 | 404 | git archive v1.2.2 | gzip > mumot-v1.2.2.tar.gz 405 | 406 | #. Publish the item in ORDA to ensure: 407 | 408 | * The release being referenceable/citable by DOI_. 409 | * The release being discoverable via the University's Library Catalogue. 410 | 411 | 412 | .. _BinderHub: https://binderhub.readthedocs.io/ 413 | .. _DOI: https://www.doi.org/ 414 | .. _MuMoT GitHub repository: https://github.com/DiODeProject/MuMoT 415 | .. _ORDA: https://www.sheffield.ac.uk/library/rdm/orda 416 | .. _Pull Request: https://help.github.com/articles/about-pull-requests/ 417 | .. _Sphinx: http://www.sphinx-doc.org/ 418 | .. _annotated tag: https://git-scm.com/book/en/v2/Git-Basics-Tagging 419 | .. _autodoc: http://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html 420 | .. _autosummary: http://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html 421 | .. _gh_actions_dashboard: https://github.com/DiODeProject/MuMoT/actions 422 | .. _gh_actions_intro: https://help.github.com/en/actions/getting-started-with-github-actions/about-github-actions 423 | .. _gh_actions_timeouts: https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions 424 | .. _mybinder.org: https://mybinder.org/ 425 | .. _nbdime: https://nbdime.readthedocs.io/ 426 | .. _nbval: https://github.com/computationalmodelling/nbval 427 | .. _numpydoc: http://numpydoc.readthedocs.io/en/latest/format.html 428 | .. _pep8: https://www.python.org/dev/peps/pep-0008/#naming-conventions 429 | .. _pytest-cov: https://pytest-cov.readthedocs.io/ 430 | .. _pytest: https://docs.pytest.org/en/latest/ 431 | .. _reStructuredText: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html 432 | .. _repo2docker: https://github.com/jupyter/repo2docker 433 | .. _semantic versioning: https://semver.org/ 434 | .. _tox: https://tox.readthedocs.io/ 435 | .. _twine: https://pypi.org/project/twine/ 436 | .. _virtualenv: https://virtualenv.pypa.io/ 437 | -------------------------------------------------------------------------------- /docs/source/faq.rst: -------------------------------------------------------------------------------- 1 | FAQ 2 | === 3 | 4 | Why does manipulating widgets create duplicate figures? 5 | ------------------------------------------------------- 6 | 7 | Please update your dependencies: versions of the `ipython` dependency older than 7.10.0 are known to cause this problem. 8 | 9 | Why are there odd line breaks after inline maths in paragraphs? 10 | --------------------------------------------------------------- 11 | 12 | This is a known issue when using Safari; try using Chrome or Firefox instead. 13 | -------------------------------------------------------------------------------- /docs/source/getting_started.rst: -------------------------------------------------------------------------------- 1 | .. _getting_started: 2 | 3 | Getting started 4 | =============== 5 | 6 | .. todo:: Add info on test notebooks and maybe static renderings of both using the nbsphinx_ Sphinx extension? 7 | 8 | The MuMoT user manual (a Jupyter Notebook) provides the most accessible introduction to working with MuMoT. 9 | 10 | MuMoT (Multiscale Modelling Tool) is a tool designed to allow sophisticated mathematical modelling and analysis, without writing equations 11 | - the class of models that can be represented is broad, ranging from chemical reaction kinetics to demography and collective behaviour 12 | - by using a web-based interactive interface with minimal coding, rapid development and exploration of models is facilitated 13 | - the tool may also be particularly useful for pedagogical demonstrations 14 | 15 | .. _mybinder_usage: 16 | 17 | Online 18 | ------ 19 | 20 | View and interact with the user manual online: 21 | 22 | .. image:: https://mybinder.org/badge.svg 23 | :alt: Start running MuMoT user manual on mybinder.org 24 | :target: https://mybinder.org/v2/gh/DiODeProject/MuMoT/v1.2.2?urlpath=tree/docs%2FMuMoTuserManual.ipynb 25 | 26 | Note that this uses the excellent and free `mybinder.org `__ service, 27 | funded by the `Gordon and Betty Moore Foundation `__, 28 | which may not always be available at times of very high demand. 29 | 30 | Also, note that the mybinder.org sessions may sometimes take several minutes to start: 31 | mybinder.org will `use a cached MuMoT environment `__ if one is available 32 | but this may not always be the case 33 | (e.g. immediately after an update to the MuMoT package). 34 | 35 | Demo notebooks 36 | -------------- 37 | The following demo notebooks are also available online: 38 | 39 | * `Paper `_: (*MuMoT authors, University of Sheffield*) 40 | * `Epidemics `_: (*Renato Pagliara, Princeton University*) 41 | * `Agent density `_: (*Yara Khaluf, Ghent University*, and *MuMoT authors, University of Sheffield*) 42 | * `COVID-19 `_: (*James A. R. Marshall, University of Sheffield*) 43 | * `Variance suppression `_: (*Andreagiovanni Reina, University of Sheffield*) 44 | * `Michaelis-Menten `_: (*Aldo Segura, University of Sheffield*) 45 | 46 | On your own machine 47 | ------------------- 48 | 49 | #. Follow the :ref:`install and post-install ` instructions. 50 | If you have already created your MuMoT conda environment or virtualenv you only need to *activate* it. 51 | #. Start a Jupyter Notebook server, unless you have done so already: 52 | 53 | .. code:: sh 54 | 55 | jupyter notebook 56 | 57 | #. Within the Jupyter file browser, 58 | browse to a clone of this Git repository, 59 | find the ``docs`` subdirectory then 60 | open ``docs/MuMoTuserManual.ipynb``, 61 | which is the MuMoT User Manual. To view demo notebooks navigate to ``DemoNotebooks``. 62 | 63 | 64 | .. _nbsphinx: https://nbsphinx.readthedocs.io/en/0.3.3/ 65 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | MuMoT: Multiscale Modelling Tool 2 | ================================ 3 | 4 | MuMoT was developed at the University of Sheffield as part of the `DiODe `__ project. 5 | 6 | MuMoT (Multiscale Modelling Tool) is a tool designed to allow sophisticated mathematical modelling and analysis, without writing equations 7 | - the class of models that can be represented is broad, ranging from chemical reaction kinetics to demography and collective behaviour 8 | - by using a web-based interactive interface with minimal coding, rapid development and exploration of models is facilitated 9 | - the tool may also be particularly useful for pedagogical demonstrations 10 | 11 | .. todo:: Add general overview (inc graphic?) 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | :hidden: 16 | 17 | self 18 | 19 | .. toctree:: 20 | :maxdepth: 1 21 | :caption: Contents: 22 | 23 | getting_started 24 | install 25 | about 26 | faq 27 | development 28 | api 29 | -------------------------------------------------------------------------------- /docs/source/install.rst: -------------------------------------------------------------------------------- 1 | .. _install: 2 | 3 | Installation 4 | ============ 5 | 6 | .. contents:: :local: 7 | 8 | Prerequisite: LaTeX 9 | ------------------- 10 | 11 | The visualisation of particular representations of models requires that you have a `LaTeX distribution`_ installed. 12 | 13 | * macOS: install MacTex_ 14 | * Linux: install TexLive_ 15 | * Windows: install MiKTeX_ or TexLive_ 16 | 17 | You can now install MuMoT :ref:`within a conda environment ` or :ref:`within a Python virtualenv `. 18 | 19 | .. _conda_inst: 20 | 21 | Installing MuMoT within a Conda environment 22 | ------------------------------------------- 23 | 24 | #. `Clone `__ 25 | `this repository `__. 26 | #. Install the conda package manager by 27 | installing a version of Miniconda_ appropriate to your operating system. 28 | #. Open a terminal within which conda is available; 29 | you can check this with 30 | 31 | .. code:: sh 32 | 33 | conda --version 34 | 35 | #. Create a new conda environment containing just Python >=3.6 e.g.: 36 | 37 | .. code:: sh 38 | 39 | conda update conda 40 | conda create -n mumot-env python=3.7 41 | 42 | #. Check that conda environment has been created: 43 | 44 | 45 | .. code:: sh 46 | 47 | conda env list 48 | 49 | ``mumot-env`` should appear in the output. 50 | 51 | #. *Activate* the environment: 52 | 53 | .. code:: sh 54 | 55 | source activate mumot-env # on macOS/Linux with older versions of conda 56 | conda activate mumot-env # on macOS/Linux with newer versions of conda 57 | activate mumot-env # on Windows 58 | 59 | #. *Install* MuMoT and dependencies into this conda environment: 60 | 61 | 62 | .. code:: sh 63 | 64 | conda install graphviz 65 | python -m pip install path/to/clone/of/MuMoT/repository 66 | 67 | NB if your clone of the MuMot repository is a subdirectory of the current directory, 68 | make sure you run ``python3 -m pip install ./MuMoT`` instead of ``python3 -m pip install MuMoT`` 69 | (to ensure MuMoT is installed from your Git clone and not :ref:`from PyPI`). 70 | 71 | .. _venv_inst: 72 | 73 | Installing MuMoT within a VirtualEnv 74 | ------------------------------------ 75 | 76 | 1. `Clone `__ 77 | `this repository `__. 78 | 2. Ensure you have the following installed: 79 | 80 | - `Python >= 3.6 `__ 81 | - the pip_ package 82 | manager (usually comes with Python 3.x but might not for certain 83 | flavours of Linux) 84 | - the virtualenv_ tool 85 | for managing Python virtual environments 86 | - graphviz_ 87 | 88 | You can check this by opening a terminal and running: 89 | 90 | .. code:: sh 91 | 92 | python3 --version 93 | python3 -m pip --version 94 | python3 -m virtualenv --version 95 | dot -V 96 | 97 | 3. Create a Python virtualenv in your home directory: 98 | 99 | .. code:: sh 100 | 101 | cd 102 | python3 -m virtualenv mumot-env 103 | 104 | 4. *Activate* this Python virtualenv: 105 | 106 | .. code:: sh 107 | 108 | source mumot-env/bin/activate # on macOS/Linux 109 | mumot-env/bin/activate # on Windows 110 | 111 | 5. *Install* MuMoT and dependencies into this Python virtualenv, then 112 | enable interactive Notebook widgets: 113 | 114 | .. code:: sh 115 | 116 | python3 -m pip install path/to/clone/of/MuMoT/repository 117 | jupyter nbextension enable --py widgetsnbextension --sys-prefix 118 | 119 | NB if your clone of the MuMot repository is a subdirectory of the current directory, 120 | make sure you run ``python3 -m pip install ./MuMoT`` instead of ``python3 -m pip install MuMoT`` 121 | (to ensure MuMoT is installed from your Git clone and not :ref:`from PyPI`). 122 | 123 | .. _pypi_inst: 124 | 125 | Installing MuMoT from PyPI 126 | -------------------------- 127 | 128 | Follow the instructions as above for 'Installing MuMoT within a VirtualEnv', but at stage 5 replace 129 | 130 | .. code:: sh 131 | 132 | python3 -m pip install path/to/clone/of/MuMoT/repository 133 | 134 | with 135 | 136 | .. code:: sh 137 | 138 | python3 -m pip install mumot 139 | 140 | (Optional) Enable tables of contents for individual Notebooks 141 | ------------------------------------------------------------- 142 | 143 | Hyperlinked tables of contents can be userful when viewing longer Notebooks such as 144 | the `MuMoT User Manual `__. 145 | 146 | Tables of contents can be displayed if you enable the **TOC2** Jupyter Extension as follows: 147 | 148 | #. Ensure the ``jupyter_contrib_nbextensions`` package is installed. 149 | This is "a collection of extensions that add functionality to the Jupyter notebook". 150 | If you installed MuMoT into a *virtualenv* using **pip** then 151 | you need to ensure that virtualenv is activated before running: 152 | 153 | .. code:: sh 154 | 155 | pip install jupyter_contrib_nbextensions 156 | 157 | #. Enable ``jupyter_contrib_nbextensions``: 158 | 159 | .. code:: sh 160 | 161 | jupyter contrib nbextension install --sys-prefix 162 | 163 | #. Enable the TOC2 ('table of contents') extension that is 164 | provided by ``jupyter_contrib_nbextensions``: 165 | 166 | .. code:: sh 167 | 168 | jupyter nbextension enable toc2/main 169 | 170 | #. Enable a graphical interface for enabling/disabling TOC2 and other 171 | Jupyter extensions. If using conda: 172 | 173 | .. code:: sh 174 | 175 | conda install -c conda-forge jupyter_nbextensions_configurator 176 | 177 | Or if using a virtualenv instead: 178 | 179 | .. code:: sh 180 | 181 | pip install jupyter_nbextensions_configurator # AND 182 | jupyter nbextensions_configurator enable --sys-prefix 183 | 184 | The next time you start Jupyter from your conda environment or virtualenv then open a Notebook 185 | you should see a table of contents displayed down the left-hand-side of the Notebook. 186 | 187 | If you subsequently want to disable the TOC2 extension 188 | and/or enable other Notebook extensions 189 | then click *Nbextensions* in the Jupyter file browser tab. 190 | 191 | .. _LaTeX distribution: https://www.latex-project.org/get/ 192 | .. _MacTex: http://www.tug.org/mactex/ 193 | .. _MiKTeX: http://miktex.org/ 194 | .. _TexLive: http://www.tug.org/texlive 195 | .. _pip: https://pip.pypa.io/en/stable/installing/ 196 | .. _virtualenv: https://virtualenv.pypa.io/en/stable/ 197 | .. _graphviz: https://graphviz.gitlab.io/download/ 198 | .. _Miniconda: https://conda.io/miniconda.html 199 | -------------------------------------------------------------------------------- /mumot/__init__.py: -------------------------------------------------------------------------------- 1 | """Multiscale Modelling Tool (MuMoT) 2 | 3 | For documentation and version information use about() 4 | 5 | Authors: 6 | James A. R. Marshall, Andreagiovanni Reina, Thomas Bose 7 | 8 | Contributors: 9 | Robert Dennison 10 | 11 | Packaging, Documentation and Deployment: 12 | Will Furnass 13 | 14 | Windows Compatibility: 15 | Renato Pagliara Vasquez 16 | """ 17 | 18 | # Set package version. 19 | # NB with Python 3.8 we could use importlib.metadata (in std lib) instead. 20 | from pkg_resources import get_distribution, DistributionNotFound 21 | try: 22 | __version__ = get_distribution(__name__).version 23 | except DistributionNotFound: 24 | # package is not installed 25 | pass 26 | import sys 27 | 28 | import matplotlib 29 | from matplotlib import pyplot as plt 30 | # Guard against iopub rate limiting warnings (https://github.com/DiODeProject/MuMoT/issues/359) 31 | from notebook.notebookapp import NotebookApp 32 | NotebookApp.iopub_msg_rate_limit = 10000.0 33 | from sympy.parsing.latex import parse_latex 34 | 35 | # Import the functions and classes we wish to export i.e. the public API 36 | from .models import ( 37 | MuMoTmodel, 38 | parseModel, 39 | ) 40 | from .utils import ( 41 | about, 42 | ) 43 | from .views import ( 44 | MuMoTSSAView, 45 | MuMoTbifurcationView, 46 | MuMoTfieldView, 47 | MuMoTintegrateView, 48 | MuMoTmultiView, 49 | MuMoTmultiagentView, 50 | MuMoTnoiseCorrelationsView, 51 | MuMoTstochasticSimulationView, 52 | MuMoTstreamView, 53 | MuMoTtimeEvolutionView, 54 | MuMoTvectorView, 55 | MuMoTview, 56 | ) 57 | from .controllers import ( 58 | MuMoTbifurcationController, 59 | MuMoTcontroller, 60 | MuMoTfieldController, 61 | MuMoTmultiController, 62 | MuMoTmultiagentController, 63 | MuMoTstochasticSimulationController, 64 | MuMoTtimeEvolutionController, 65 | ) 66 | from .consts import ( 67 | NetworkType, 68 | MAX_RANDOM_SEED, 69 | ) 70 | from .exceptions import ( 71 | MuMoTError, 72 | MuMoTSyntaxError, 73 | MuMoTValueError, 74 | MuMoTWarning, 75 | ) 76 | 77 | try: 78 | # Try to get the currently-running IPython instance 79 | ipython = get_ipython() 80 | ipython.magic('alias_magic model latex') 81 | ipython.magic('matplotlib nbagg') 82 | 83 | def _hide_traceback(exc_tuple=None, filename=None, tb_offset=None, 84 | exception_only=False, running_compiled_code=False): 85 | etype, value, tb = sys.exc_info() 86 | return ipython._showtraceback(etype, value, ipython.InteractiveTB.get_exception_only(etype, value)) 87 | 88 | _show_traceback = ipython.showtraceback 89 | ipython.showtraceback = _hide_traceback 90 | except NameError: 91 | # There is no currently-running IPython instance 92 | pass 93 | 94 | 95 | def setVerboseExceptions(verbose: bool = True) -> None: 96 | """Set the verbosity of exception handling. 97 | 98 | Parameters 99 | ---------- 100 | verbose : bool, optional 101 | Whether to show a exception traceback. Defaults to True. 102 | 103 | """ 104 | ipython.showtraceback = _show_traceback if verbose else _hide_traceback 105 | -------------------------------------------------------------------------------- /mumot/consts.py: -------------------------------------------------------------------------------- 1 | """Constants (inc. symbols) used in MuMoT.""" 2 | 3 | from enum import Enum 4 | 5 | from sympy.parsing.latex import parse_latex 6 | 7 | MAX_RANDOM_SEED = 2147483647 8 | 9 | EMPTYSET_SYMBOL = parse_latex('1') 10 | 11 | GREEK_LETT_LIST_1 = ['alpha', 'beta', 'gamma', 'Gamma', 'delta', 'Delta', 12 | 'epsilon', 'zeta', 'theta', 'Theta', 'iota', 'kappa', 13 | 'lambda', 'Lambda', 'mu', 'xi', 'Xi', 'pi', 'Pi', 'rho', 14 | 'sigma', 'Sigma', 'tau', 'upsilon', 'Upsilon', 'phi', 15 | 'Phi', 'chi', 'psi', 'Psi', 'omega', 'Omega', 'varrho', 16 | 'vartheta', 'varepsilon', 'varphi'] 17 | GREEK_LETT_LIST_2 = ['\\' + GreekLett for GreekLett in GREEK_LETT_LIST_1] 18 | GREEK_LETT_RESERVED_LIST = ['\\eta', '\\nu', '\\Phi', '(\\eta)', '(\\nu)', 19 | '(\\Phi)'] 20 | GREEK_LETT_RESERVED_LIST_PRINT = ['eta', 'nu', 'Phi'] 21 | 22 | INITIAL_RATE_VALUE = 0.5 23 | RATE_BOUND = 10.0 24 | RATE_STEP = 0.1 25 | 26 | INITIAL_COND_INIT_VAL = 0.0 27 | INITIAL_COND_INIT_BOUND = 1.0 28 | 29 | LINE_COLOR_LIST = ['b', 'g', 'r', 'c', 'm', 'y', 'grey', 'orange', 'k'] 30 | MULTIPLOT_COLUMNS = 2 31 | 32 | 33 | class NetworkType(Enum): 34 | """Enumeration of possible network types.""" 35 | 36 | FULLY_CONNECTED = 0 37 | ERSOS_RENYI = 1 38 | BARABASI_ALBERT = 2 39 | SPACE = 3 40 | DYNAMIC = 4 41 | -------------------------------------------------------------------------------- /mumot/defaults.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | 4 | class MuMoTdefault: 5 | """Store default parameters.""" 6 | 7 | _initialRateValue = 2 # @todo: was 1 (choose initial values sensibly) 8 | _rateLimits = (0.0, 20.0) # @todo: choose limit values sensibly 9 | _rateStep = 0.1 # @todo: choose rate step sensibly 10 | 11 | @staticmethod 12 | def setRateDefaults(initRate=_initialRateValue, 13 | limits: Tuple[float, float] = _rateLimits, 14 | step: float = _rateStep) -> None: 15 | MuMoTdefault._initialRateValue = initRate 16 | MuMoTdefault._rateLimits = limits 17 | MuMoTdefault._rateStep = step 18 | 19 | _maxTime = 3 20 | _timeLimits = (0, 10) 21 | _timeStep = 0.1 22 | # _maxTime = 5 23 | # _timeLimits = (0, 50) 24 | # _timeStep = 0.5 25 | 26 | @staticmethod 27 | def setTimeDefaults(initTime=_maxTime, limits=_timeLimits, 28 | step=_timeStep) -> None: 29 | MuMoTdefault._maxTime = initTime 30 | MuMoTdefault._timeLimits = limits 31 | MuMoTdefault._timeStep = step 32 | 33 | _agents = 1.0 34 | _agentsLimits = (0.0, 1.0) 35 | _agentsStep = 0.01 36 | 37 | @staticmethod 38 | def setAgentsDefaults(initAgents: float = _agents, 39 | limits: Tuple[float, float] = _agentsLimits, 40 | step: float = _agentsStep) -> None: 41 | MuMoTdefault._agents = initAgents 42 | MuMoTdefault._agentsLimits = limits 43 | MuMoTdefault._agentsStep = step 44 | 45 | _systemSize = 10 46 | _systemSizeLimits = (5, 100) 47 | _systemSizeStep = 1 48 | 49 | @staticmethod 50 | def setSystemSizeDefaults(initSysSize: int = _systemSize, 51 | limits: Tuple[int, int] = _systemSizeLimits, 52 | step: int = _systemSizeStep) -> None: 53 | MuMoTdefault._systemSize = initSysSize 54 | MuMoTdefault._systemSizeLimits = limits 55 | MuMoTdefault._systemSizeStep = step 56 | 57 | _plotLimits = 1.0 58 | _plotLimitsLimits = (0.1, 5.0) 59 | _plotLimitsStep = 0.1 60 | 61 | @staticmethod 62 | def setPlotLimitsDefaults(initPlotLimits: float = _plotLimits, 63 | limits: Tuple[float, float] = _plotLimitsLimits, 64 | step: float = _plotLimitsStep) -> None: 65 | MuMoTdefault._plotLimits = initPlotLimits 66 | MuMoTdefault._plotLimitsLimits = limits 67 | MuMoTdefault._plotLimitsStep = step 68 | -------------------------------------------------------------------------------- /mumot/exceptions.py: -------------------------------------------------------------------------------- 1 | """MuMoT warning, exception and error classes.""" 2 | 3 | class MuMoTWarning(Warning): 4 | """Class to report MuMoT-specific warnings. 5 | """ 6 | pass 7 | 8 | 9 | class MuMoTError(Exception): 10 | """Class to report MuMoT-specific errors. 11 | """ 12 | pass 13 | 14 | 15 | class MuMoTValueError(MuMoTError): 16 | """Class to report MuMoT-specific errors arising from incorrect input. 17 | """ 18 | pass 19 | 20 | 21 | class MuMoTSyntaxError(MuMoTError): 22 | """Class to report MuMoT-specific errors arising from incorrectly-structured input. 23 | """ 24 | pass 25 | 26 | 27 | def _raiseModelError(expected, read, rule): 28 | raise MuMoTSyntaxError(f"Expected {expected} but read '{read}' in rule: {rule}") 29 | -------------------------------------------------------------------------------- /postBuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Not needed if installed ipywidgets using conda 4 | jupyter nbextension enable --py widgetsnbextension --sys-prefix 5 | 6 | # Enable per-Notebook table of contents in Jupyter UI 7 | # 1: Install jupyter_contrib_nbextensions package 8 | pip install --upgrade-strategy only-if-needed jupyter_contrib_nbextensions 9 | # 2: Configure jupyter_contrib_nbextensions: the following "copies the 10 | # nbextensions' javascript and css files into the jupyter server's 11 | # search directory" 12 | jupyter contrib nbextension install --sys-prefix 13 | # 3: Enable the TOC2 extension 14 | jupyter nbextension enable toc2/main 15 | # 4: Enable Nbextensions tab in Jupyter UI; should not be necessary if installed nbextensions_configurator using conda 16 | jupyter nbextensions_configurator enable --sys-prefix 17 | 18 | # run matplotlib once to generate the font cache 19 | python -c "import matplotlib as mpl; mpl.use('Agg'); import matplotlib.pyplot as plt; fig, ax = plt.subplots(); fig.savefig('test.png')" 20 | 21 | test -e test.png && rm test.png 22 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=30.3.0", "wheel", "setuptools_scm"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.setuptools_scm] 6 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | # Need to use latest RTD image for Python 3.6 support 2 | build: 3 | image: latest 4 | 5 | python: 6 | version: 3.6 7 | extra_requirements: 8 | - docs 9 | 10 | # Don't build any extra formats 11 | formats: [] 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | . 2 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.6 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = mumot 3 | description = Multiscale Modelling Tool 4 | long_description = file: README.md 5 | long_description_content_type = text/markdown 6 | url = https://github.com/DiODeProject/MuMoT 7 | author = James A. R. Marshall, Andreagiovanni Reina, Thomas Bose 8 | author_email = james.marshall@shef.ac.uk 9 | license = GPL-3.0 10 | license_file = LICENSE 11 | classifiers = 12 | License :: OSI Approved :: GNU General Public License v3 (GPLv3) 13 | Operating System :: OS Independent 14 | Programming Language :: Python 15 | Programming Language :: Python :: 3.6 16 | Programming Language :: Python :: 3.7 17 | Programming Language :: Python :: 3.8 18 | Topic :: Scientific/Engineering 19 | Topic :: Scientific/Engineering :: Chemistry 20 | Topic :: Scientific/Engineering :: Mathematics 21 | Topic :: Scientific/Engineering :: Physics 22 | 23 | [options] 24 | zip_safe = False 25 | include_package_data = True 26 | packages = find: 27 | install_requires = 28 | antlr4-python3-runtime >=4.7,<4.8 29 | graphviz 30 | setuptools # used with Python <3.8 for finding pkg version at runtime (with 3.8 could use importlib.metadata) 31 | ipykernel 32 | ipython >=7.10.0 # minimum required to avoid creating duplicate Figures when changing ipywidget states 33 | ipywidgets 34 | matplotlib 35 | networkx 36 | notebook 37 | pydstool >=0.90.3 # min version that allows scipy >= 1.0.0 to be used 38 | pyzmq 39 | scipy 40 | sympy >=1.4,<1.5 # pinned to <1.5 due to Issue #377 41 | tornado 42 | setup_requres = 43 | setuptools_scm 44 | 45 | [options.extras_require] 46 | test = 47 | pytest 48 | pytest-cov 49 | nbval >=0.9.5 50 | nbdime 51 | jupyter 52 | docs = 53 | sphinx 54 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | 4 | setup( 5 | use_scm_version=True, 6 | ) 7 | -------------------------------------------------------------------------------- /tests/test_all.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from mumot.models import parseModel 4 | 5 | EXPRESSION_STRS = [ 6 | "U -> A : g_A", 7 | "U -> B : g_B", 8 | "A -> U : a_A", 9 | "B -> U : a_B", 10 | "A + U -> A + A : r_A", 11 | "B + U -> B + B : r_B", 12 | "A + B -> A + U : s", 13 | "A + B -> B + U : s"] 14 | 15 | 16 | def test_parse_model_from_cell_contents(): 17 | """Assert we can instantiate a MuMoTmodel from the contents of a Notebook 18 | cell that uses the %%model cell magic.""" 19 | parseModel(r"\n".join( 20 | ["get_ipython().run_cell_magic('model', '', '$"] + 21 | EXPRESSION_STRS + 22 | ["$", "')"])) 23 | 24 | 25 | def test_parse_model_from_str(): 26 | """Assert we can instantiate a MuMoTmodel from a multi-line string.""" 27 | parseModel(os.linesep.join(EXPRESSION_STRS)) 28 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py36, py37, py38 3 | isolated_build = True 4 | 5 | [testenv] 6 | extras = 7 | test 8 | docs 9 | passenv = DISPLAY BROWSER TOXENV 10 | install_command = pip install {opts} {packages} 11 | deps = 12 | codecov>=1.4.0 13 | coverage 14 | whitelist_externals = 15 | bash 16 | test 17 | wc 18 | make 19 | commands = 20 | ; Ensure ipywidgets Jupyter extension is installed 21 | jupyter nbextension enable --py --sys-prefix widgetsnbextension 22 | 23 | ; Run unit tests and generate code coverage report 24 | pytest --verbose --cov-config=tox.ini --cov="{envsitepackagesdir}/mumot" tests 25 | 26 | ; Ensure test Notebooks runs *without errors* (do not check for regressions yet) 27 | ; (add --nbdime if running tox locally and want to visualise/explore diffs in web browser) 28 | pytest --verbose --maxfail=1 --nbval-lax --nbval-cell-timeout=120 --cov-config=tox.ini --cov="{envsitepackagesdir}/mumot" --cov-append \ 29 | TestNotebooks/MuMoTtest.ipynb \ 30 | TestNotebooks/MiscTests/MuMoTtest_GreekLetters.ipynb \ 31 | TestNotebooks/MiscTests/MuMoTtest_MultiController.ipynb \ 32 | TestNotebooks/MiscTests/MuMoTtest_NoiseFixedPoints.ipynb \ 33 | TestNotebooks/MiscTests/MuMoTtest_oneDimensionalModels.ipynb 34 | 35 | ; Ensure the user manual Notebook runs *without errors* (do not check for regressions yet) 36 | ; (add --nbdime if running tox locally and want to visualise/explore diffs in web browser) 37 | pytest --verbose --maxfail=1 --nbval-lax --nbval-cell-timeout=120 docs/MuMoTuserManual.ipynb 38 | 39 | ; Additional example Notebooks (TODO; leave commented) 40 | ; TestNotebooks/MiscTests/MuMoTtest_MasterEq.ipynb - currently hangs after showNoiseEqs() for second model? 41 | ; TestNotebooks/MiscTests/MuMoTtest_bifurcation.ipynb 42 | ; TestNotebooks/MiscTests/MuMoTuserManual_for_LabPresentation.ipynb 43 | 44 | ; Ensure the user manual and regression test Notebooks do not show regressions (TODO; leave commented) 45 | ; (add --nbdime if running tox locally and want to visualise/explore diffs in web browser) 46 | ; pytest --verbose --maxfail=1 --nbval --nbval-cell-timeout=120 --cov-config=tox.ini --cov="{envsitepackagesdir}/mumot" --cov-append TestNotebooks/MuMoTtest.ipynb 47 | 48 | ; Check user manual does not contain output cells 49 | bash -c 'test $(nbshow --outputs docs/MuMoTuserManual.ipynb | wc -c) -eq 0' 50 | 51 | ; Check we can build the docs 52 | make --directory docs html 53 | 54 | ; Used by pytest-cov 55 | [run] 56 | branch = True 57 | source = mumot 58 | ; Do not calculate coverage of third-party code. 59 | ; (NB here this is the _installed_ code in the hidden virtualenv created by tox, 60 | ; not the source code of the package) 61 | omit = 62 | */site-packages/mumot/gen/* 63 | 64 | ; Used by pytest-cov 65 | [report] 66 | exclude_lines = 67 | if self.debug: 68 | pragma: no cover 69 | raise NotImplementedError 70 | if __name__ == .__main__.: 71 | ignore_errors = True 72 | --------------------------------------------------------------------------------