{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Create a model\n", "\n", "In this package, probabilistic models are represented by `ContinuousTimeModel` objects. There are several ways to create these kind of objects." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using MarkovProcesses" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. With load_model()\n", "\n", "A bunch of models are already available in the package. If `str_model::String` is the name of an implemented model, then `load_model(str_model)` creates a variable with name `str_model`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Poisson process\n", "load_model(\"poisson\")\n", "poisson" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Repressilator model\n", "load_model(\"repressilator\")\n", "repressilator" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. With @network_model\n", "\n", "A useful feature is avaiable for CTMCs induced by a Chemical Reaction Network. Let's consider the Chemical Reaction Network of the SIR model:\n", "\n", "$$\n", "Infection: S + I \\xrightarrow{k_i} 2I \\\\\n", "Recovery: I \\xrightarrow{k_r} R\n", "$$\n", "\n", "The macro `@network_model` creates easily a CTMC stored in a `ContinuousTimeModel` variable based on this formalism." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "easy_sir = @network_model begin\n", " Infection: (S + I => 2I, ki*I*S)\n", " Recovery: (I => R, kr*I)\n", "end \"My awesome SIR\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the first reaction, `ki*I*S` is the reaction rate of the reaction `Infection`. This model is almost ready to use, we have to set the initial state and the parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "set_param!(easy_sir, [0.0012, 0.05])\n", "set_x0!(easy_sir, [95, 5, 0])\n", "using Plots\n", "σ = simulate(easy_sir)\n", "plot(times(σ), σ.I, linetype = :steppost)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Manually (advanced)\n", "\n", "When the above cases don't fit your application one can create manually a `ContinuousTimeModel`. Let's take a look about the signature of the constructor method:\n", "\n", "```julia\n", "function ContinuousTimeModel(dim_state::Int, dim_params::Int, map_var_idx::Dict{VariableModel,Int}, \n", " map_param_idx::Dict{ParameterModel,Int}, transitions::Vector{<:Transition},\n", " p::Vector{Float64}, x0::Vector{Int}, t0::Float64, \n", " f!::Function, isabsorbing::Function; kwargs)\n", "```\n", "\n", "First, one has to specify the dimensions of the state space and the parameter space." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dim_state_sir, dim_params_sir = 3, 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`map_var_idx` is a dictionary that maps each model variable (represented by a `Symbol`) to an index in the state space." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "map_var_idx_sir = Dict(:S => 1, :I => 2, :R => 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`map_var_params` is the equivalent of `map_var_idx` for parameters." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "map_params_idx_sir = Dict(:ki => 1, :kr => 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`transitions` are the transitions/reactions of the model (vector of `Symbol`), `p`, `x0` and `t0` are respectively the parameters, the initial state and initial time of the model." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "transitions_sir = [:Infection, :Recovery]\n", "p_sir = [0.0012, 0.05]\n", "x0_sir = [95, 5, 0]\n", "t0_sir = 0.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The two last arguments are functions, the first one, called `f!` must have the signature:\n", "\n", "```julia\n", "function f!(xnplus1::Vector{Int}, ptr_t::Vector{Float64}, ptr_tr::Vector{Transition},\n", " xn::Vector{Int}, tn::Float64, p::Vector{Float64})\n", "\n", "```\n", "\n", "It should return nothing. `xnplus1`, `ptr_t` and `ptr_tr` are vectors where the next values are stored. `ptr_t` is of length 1 and stores the next time value (`ptr_t[1] = tn + delta_t`) whereas `ptr_tr` stores the name of the next transition/reaction (`ptr_tr[1] = :Infection` for example). This function is implemented in the package as:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@everywhere function sir_f!(xnplus1::Vector{Int}, l_t::Vector{Float64}, l_tr::Vector{Transition},\n", " xn::Vector{Int}, tn::Float64, p::Vector{Float64})\n", " @inbounds a1 = p[1] * xn[1] * xn[2]\n", " @inbounds a2 = p[2] * xn[2]\n", " l_a = (a1, a2)\n", " asum = sum(l_a)\n", " if asum == 0.0\n", " copyto!(xnplus1, xn)\n", " return nothing\n", " end\n", " nu_1 = (-1, 1, 0)\n", " nu_2 = (0, -1, 1)\n", " l_nu = (nu_1, nu_2)\n", " l_str_R = (:Infection, :Recovery)\n", "\n", " u1 = rand()\n", " u2 = rand()\n", " tau = - log(u1) / asum\n", " b_inf = 0.0\n", " b_sup = a1\n", " reaction = 0\n", " for i = 1:2 \n", " if b_inf < asum*u2 < b_sup\n", " reaction = i\n", " break\n", " end\n", " @inbounds b_inf += l_a[i]\n", " @inbounds b_sup += l_a[i+1]\n", " end\n", " \n", " nu = l_nu[reaction]\n", " for i = 1:3\n", " @inbounds xnplus1[i] = xn[i]+nu[i]\n", " end\n", " @inbounds l_t[1] = tn + tau\n", " @inbounds l_tr[1] = l_str_R[reaction]\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The second function called `isaborbing` must have the signature:\n", "\n", "```julia\n", "isabsorbing(p::Vector{Float64}, xn::Vector{Int})\n", "```\n", "\n", "This function checks if the state `xn` is an absorbing state according to the model parametrised by `p`. It has to return true or false.\n", "\n", "For a CTMC, a state is an absorbing state if the total exit rate is zero. In the case of the SIR model:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@everywhere sir_isabsorbing(p::Vector{Float64}, xn::Vector{Int}) = (p[1]*xn[1]*xn[2] + p[2]*xn[2]) === 0.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally one sets the observed variables and the model can be created." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "g_sir = [:I]\n", "\n", "# Generates simulate method for the new model\n", "@everywhere @eval $(MarkovProcesses.generate_code_model_type_def(:TryhardSIRModel))\n", "@everywhere @eval $(MarkovProcesses.generate_code_model_type_constructor(:TryhardSIRModel))\n", "@everywhere @eval $(MarkovProcesses.generate_code_simulation(:TryhardSIRModel, :sir_f!, :sir_isabsorbing))\n", "\n", "\n", "tryhard_sir = TryhardSIRModel(dim_state_sir, dim_params_sir, \n", " map_var_idx_sir, map_params_idx_sir, \n", " transitions_sir, p_sir, x0_sir, t0_sir, \n", " :sir_f!, :sir_isabsorbing; g = g_sir)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using Plots\n", "σ = simulate(tryhard_sir)\n", "plot(times(σ), σ.I, linetype = :steppost)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.5.3", "language": "julia", "name": "julia-1.5" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.5.3" } }, "nbformat": 4, "nbformat_minor": 4 }