From 2c9a7ef3102718a598d35b029c5d837aa7367ffa Mon Sep 17 00:00:00 2001 From: xiaoming Date: Thu, 5 Jan 2023 09:39:24 +0100 Subject: [PATCH 01/11] change fastmath --- src/DelaySSAToolkit.jl | 2 +- src/delayaggregator/delaydirect.jl | 20 ++++++++++---------- src/delayaggregator/delayrejection.jl | 6 +++--- src/delayaggregator/delayssajump.jl | 10 +++++----- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/DelaySSAToolkit.jl b/src/DelaySSAToolkit.jl index 35fdd1f2..8b7c5805 100644 --- a/src/DelaySSAToolkit.jl +++ b/src/DelaySSAToolkit.jl @@ -4,7 +4,7 @@ using Reexport @reexport using DiffEqBase using UnPack, Random, LinearAlgebra, DiffEqBase, StaticArrays, DocStringExtensions using FunctionWrappers, RandomNumbers - +using Base.FastMath: add_fast, sub_fast, exp_fast, log_fast, div_fast import DiffEqBase: DiscreteCallback, init, solve, solve!, initialize! # Types and Structs diff --git a/src/delayaggregator/delaydirect.jl b/src/delayaggregator/delaydirect.jl index 5a90a5f5..47da85d7 100644 --- a/src/delayaggregator/delaydirect.jl +++ b/src/delayaggregator/delaydirect.jl @@ -53,7 +53,7 @@ end function generate_jumps!(p::DelayDirectJumpAggregation, integrator, u, params, t) generate_time_to_next_jump!(p, integrator, params, t) - @fastmath p.next_jump_time = t + p.time_to_next_jump + p.next_jump_time = add_fast(t, p.time_to_next_jump) @inbounds p.next_jump = searchsortedfirst(p.cur_rates, rand(p.rng) * p.sum_rate) # nothing end @@ -97,17 +97,17 @@ end aₜ_ = aₜ # backup aₜ aₜ += shadow_integrator.cur_rates[end] * (p.next_delay_time) - F = one(t) - exp(-aₜ) + F = sub_fast(one(t), exp_fast(-aₜ)) end sum_ = p.copied ? shadow_integrator.cur_rates[end] : p.sum_rate - ttnj_last = (-log(one(t) - r1) - aₜ_) / sum_ - ttnj = prev_T1 + ttnj_last + ttnj_last = div_fast(sub_fast(-log_fast(one(t) - r1), aₜ_), sum_) + ttnj = add_fast(prev_T1, ttnj_last) end if p.copied shift_delay_channel!(shadow_integrator.de_chan, ttnj_last) update_delay_channel!(shadow_integrator.de_chan) - fill_cum_rates_and_sum!(p, shadow_integrator.u, params, t + ttnj) + fill_cum_rates_and_sum!(p, shadow_integrator.u, params, add_fast(t, ttnj)) end p.time_to_next_jump = ttnj nothing @@ -136,7 +136,7 @@ function update_delay_at_tstop_test!(p, integrator, params, t, tgap) prev_T1 = cur_T1 # to avoid cur_T1 = Inf cur_T1 += p.next_delay_time end - ttnj_last = tgap - prev_T1 + ttnj_last = sub_fast(tgap, prev_T1) shift_delay_channel!(integrator.de_chan, ttnj_last) update_delay_channel!(integrator.de_chan) nothing @@ -156,7 +156,7 @@ function fill_cum_rates_and_sum!(p::DelayDirectJumpAggregation, u, params, t) idx = get_num_majumps(majumps) @inbounds for i in 1:idx new_rate = evalrxrate(u, i, majumps) - cur_rates[i] = new_rate + prev_rate + cur_rates[i] = add_fast(prev_rate, new_rate) prev_rate = cur_rates[i] end # constant jump rates @@ -164,7 +164,7 @@ function fill_cum_rates_and_sum!(p::DelayDirectJumpAggregation, u, params, t) rates = p.rates @inbounds for i in eachindex(p.rates) new_rate = rates[i](u, params, t) - cur_rates[idx] = new_rate + prev_rate + cur_rates[idx] = add_fast(prev_rate, new_rate) prev_rate = cur_rates[idx] idx += 1 end @@ -184,7 +184,7 @@ function calculate_sum_rate!(p, s::ShadowIntegrator, u, params, t) idx = get_num_majumps(majumps) @inbounds for i in 1:idx new_rate = evalrxrate(u, i, majumps) - cur_rates[i] = new_rate + prev_rate + cur_rates[i] = add_fast(prev_rate, new_rate) prev_rate = cur_rates[i] end # constant jump rates @@ -192,7 +192,7 @@ function calculate_sum_rate!(p, s::ShadowIntegrator, u, params, t) rates = p.rates @inbounds for i in eachindex(rates) new_rate = rates[i](u, params, t) - cur_rates[idx] = new_rate + prev_rate + cur_rates[idx] = add_fast(prev_rate, new_rate) prev_rate = cur_rates[idx] idx += 1 end diff --git a/src/delayaggregator/delayrejection.jl b/src/delayaggregator/delayrejection.jl index 7f6aaa42..0d1c0471 100644 --- a/src/delayaggregator/delayrejection.jl +++ b/src/delayaggregator/delayrejection.jl @@ -58,7 +58,7 @@ function generate_jumps!(p::DelayRejectionJumpAggregation, integrator, u, params nothing end -@fastmath function time_to_next_jump!(p::DelayRejectionJumpAggregation, integrator, u, params, t) +function time_to_next_jump!(p::DelayRejectionJumpAggregation, integrator, u, params, t) prev_rate = zero(t) new_rate = zero(t) cur_rates = p.cur_rates @@ -67,7 +67,7 @@ end idx = get_num_majumps(majumps) @inbounds for i in 1:idx new_rate = evalrxrate(u, i, majumps) - cur_rates[i] = new_rate + prev_rate + cur_rates[i] = add_fast(new_rate, prev_rate) prev_rate = cur_rates[i] end # constant jump rates @@ -75,7 +75,7 @@ end rates = p.rates @inbounds for i in eachindex(p.rates) new_rate = rates[i](u, params, t) - cur_rates[idx] = new_rate + prev_rate + cur_rates[idx] = add_fast(new_rate, prev_rate) prev_rate = cur_rates[idx] idx += 1 end diff --git a/src/delayaggregator/delayssajump.jl b/src/delayaggregator/delayssajump.jl index 81b7b03b..91387324 100644 --- a/src/delayaggregator/delayssajump.jl +++ b/src/delayaggregator/delayssajump.jl @@ -122,7 +122,7 @@ Recalculate the rate for the jump with index `rx`. end -@inline @fastmath function evalrxrate(speciesvec::AbstractVector{T}, rxidx::S,majump::MassActionJump{U,V,W,X})::R where {T,S,R,U <: AbstractVector{R},V,W,X} +@inline function evalrxrate(speciesvec::AbstractVector{T}, rxidx::S,majump::MassActionJump{U,V,W,X})::R where {T,S,R,U <: AbstractVector{R},V,W,X} val = one(T) @inbounds for specstoch in majump.reactant_stoch[rxidx] specpop = speciesvec[specstoch[1]] @@ -135,7 +135,7 @@ end @inbounds return val * majump.scaled_rates[rxidx] end -@inline @fastmath function executerx!(speciesvec::AbstractVector{T}, rxidx::S,majump::M) where {T,S,M <: JumpProcesses.AbstractMassActionJump} +@inline function executerx!(speciesvec::AbstractVector{T}, rxidx::S,majump::M) where {T,S,M <: JumpProcesses.AbstractMassActionJump} @inbounds net_stoch = majump.net_stoch[rxidx] @inbounds for specstoch in net_stoch speciesvec[specstoch[1]] += specstoch[2] @@ -143,7 +143,7 @@ end nothing end -@inline @fastmath function executerx(speciesvec::SVector{T}, rxidx::S,majump::M) where {T,S,M <: JumpProcesses.AbstractMassActionJump} +@inline function executerx(speciesvec::SVector{T}, rxidx::S,majump::M) where {T,S,M <: JumpProcesses.AbstractMassActionJump} @inbounds net_stoch = majump.net_stoch[rxidx] @inbounds for specstoch in net_stoch speciesvec = setindex(speciesvec,speciesvec[specstoch[1]]+specstoch[2],specstoch[1]) @@ -177,7 +177,7 @@ end @inbounds executerx!(u, next_jump, ma_jumps) end else - idx = next_jump - num_ma_rates + idx = sub_fast(next_jump, num_ma_rates) @inbounds p.affects![idx](integrator) end # shift delay channel ! @@ -213,7 +213,7 @@ function compare_delay!(p::AbstractDSSAJumpAggregator, de_chan, dt_delay, dt_rea p.time_to_next_jump = ttnj p.next_delay = next_delay p.num_next_delay = num_next_delay - @fastmath p.next_jump_time = t + ttnj + p.next_jump_time = add_fast(t, ttnj) nothing end From 02a3d9fd619f82589daabfe9efff0b54009781ca Mon Sep 17 00:00:00 2001 From: xiaoming Date: Thu, 5 Jan 2023 09:43:51 +0100 Subject: [PATCH 02/11] fix some parsing error --- docs/src/index.md | 4 ++-- docs/src/tutorials/heterogeneous_delay.md | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 832995c6..76715edb 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -20,8 +20,8 @@ DelaySSAToolkit.jl is a tool developed on top of [JumpProcesses.jl](https://gith DelaySSAToolkit can be installed through the Julia package manager: ```julia -]add DelaySSAToolkit -using DelaySSAToolkit +using Pkg +Pkg.add("DelaySSAToolkit") ``` and you might need to run ```julia diff --git a/docs/src/tutorials/heterogeneous_delay.md b/docs/src/tutorials/heterogeneous_delay.md index f33968fa..da8aa7ce 100644 --- a/docs/src/tutorials/heterogeneous_delay.md +++ b/docs/src/tutorials/heterogeneous_delay.md @@ -78,9 +78,6 @@ ensprob2 = EnsembleProblem(jprob, prob_func = prob_func) Note that a simulation of $10^4$ samples with very high production number (up to ~1000 for X and ~3000 for Y) only takes few minutes on a laptop: ```julia-repl julia> @time ens2 = solve(ensprob2, SSAStepper(), EnsembleThreads(), trajectories = 10^4) - - 78.925908 seconds (249.65 M allocations: 28.632 GiB, 6.60% gc time) -EnsembleSolution Solution of length 10000 with uType ``` Here we plot the histogram of the number of unfinished reactant $X$s in the delay channel. From 383295f743cf3c0fd618ae8f3f884fac1ffd483e Mon Sep 17 00:00:00 2001 From: xiaoming Date: Thu, 5 Jan 2023 10:46:53 +0100 Subject: [PATCH 03/11] issue with dep_graph_delay --- examples/bursty.jl | 3 +- src/DelaySSAToolkit.jl | 3 +- src/delayaggregator/aggregators.jl | 17 +- src/delayaggregator/delaycoevolve.jl | 259 +++++++++++++++++++++++++++ test/bursty_model.jl | 2 +- test/cascade_of_delay_reaction.jl | 2 +- test/dep_gr_delay.jl | 2 +- test/low_level_interface.jl | 24 ++- test/save_delay_channel.jl | 2 +- 9 files changed, 305 insertions(+), 9 deletions(-) create mode 100644 src/delayaggregator/delaycoevolve.jl diff --git a/examples/bursty.jl b/examples/bursty.jl index c7448aaa..01dadf83 100644 --- a/examples/bursty.jl +++ b/examples/bursty.jl @@ -35,7 +35,8 @@ delay_trigger_affect! delay_trigger = Dict([Pair(i, delay_trigger_affect![i]) for i in 1:burst_sup]) delay_complete = Dict(1=>[1=>-1]) delay_interrupt = Dict() -alg = DelayRejection() +# alg = DelayRejection() +alg = DelayCoevolve() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) jprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel = false) diff --git a/src/DelaySSAToolkit.jl b/src/DelaySSAToolkit.jl index 8b7c5805..bc8cbaa5 100644 --- a/src/DelaySSAToolkit.jl +++ b/src/DelaySSAToolkit.jl @@ -38,12 +38,13 @@ include("delayaggregator/delayrejection.jl") include("delayaggregator/delaymnrm.jl") include("delayaggregator/delaydirect.jl") include("delayaggregator/delaydirectCR.jl") +include("delayaggregator/delaycoevolve.jl") include("delaySSA_stepper.jl") include("utils.jl") # export DSSAIntegrator export DelayJumpProblem, DelayJumpSet, SSAStepper, MassActionJump, ConstantRateJump, JumpProblem, JumpSystem, JumpSet -export DelayRejection, DelayMNRM, DelayDirect, DelayDirectCR +export DelayRejection, DelayMNRM, DelayDirect, DelayDirectCR, DelayCoevolve export solve, remake end diff --git a/src/delayaggregator/aggregators.jl b/src/delayaggregator/aggregators.jl index 67d388da..924154b1 100644 --- a/src/delayaggregator/aggregators.jl +++ b/src/delayaggregator/aggregators.jl @@ -33,6 +33,19 @@ A modifed version of the Delay Next Reaction Method from David F. Anderson, "A modified Next Reaction Method for simulating chemical systems with time dependent propensities and delays", The Journal of Chemical Physics 128, 109903(2008). """ struct DelayMNRM <: AbstractDelayAggregatorAlgorithm end +""" +$(TYPEDEF) +An adaptaton of the COEVOLVE algorithm for simulating any compound jump process +that evolves through time. This method handles variable intensity rates with +user-defined bounds and inter-dependent processes. It reduces to NRM when rates +are constant. +M. Farajtabar, Y. Wang, M. Gomez-Rodriguez, S. Li, H. Zha, and L. Song, +COEVOLVE: a joint point process model for information diffusion and network +evolution, Journal of Machine Learning Research 18(1), 1305–1353 (2017). doi: +10.5555/3122009.3122050. +This is a further adaptation from https://github.com/SciML/JumpProcesses.jl/pull/276 with time delays implementation. +""" +struct DelayCoevolve <: AbstractDelayAggregatorAlgorithm end needs_vartojumps_map(aggregator::AbstractDelayAggregatorAlgorithm) = false needs_depgraph(aggregator::AbstractDelayAggregatorAlgorithm) = false @@ -40,4 +53,6 @@ needs_depgraph(aggregator::AbstractDelayAggregatorAlgorithm) = false needs_depgraph(aggregator::DelayMNRM) = true needs_vartojumps_map(aggregator::DelayMNRM) = true needs_depgraph(aggregator::DelayDirectCR) = true -needs_vartojumps_map(aggregator::DelayDirectCR) = true \ No newline at end of file +needs_vartojumps_map(aggregator::DelayDirectCR) = true +needs_depgraph(aggregator::DelayCoevolve) = true +needs_vartojumps_map(aggregator::DelayCoevolve) = true \ No newline at end of file diff --git a/src/delayaggregator/delaycoevolve.jl b/src/delayaggregator/delaycoevolve.jl new file mode 100644 index 00000000..4f6b5d9e --- /dev/null +++ b/src/delayaggregator/delaycoevolve.jl @@ -0,0 +1,259 @@ +""" +Queue method. This method handles variable intensity rates. +""" +mutable struct DelayCoevolveJumpAggregation{T, S, F1, F2, RNG, GR, PQ} <: + AbstractDSSAJumpAggregator + next_jump::Int # the next jump to execute + prev_jump::Int # the previous jump that was executed + next_jump_time::T # the time of the next jump + end_time::T # the time to stop a simulation + cur_rates::Vector{T} # the last computed upper bound for each rate + sum_rate::Nothing # not used + ma_jumps::S # MassActionJumps + rates::F1 # vector of rate functions + affects!::F2 # vector of affect functions for VariableRateJumps + save_positions::Tuple{Bool, Bool} # tuple for whether to save the jumps before and/or after event + rng::RNG # random number generator + dep_gr::GR # map from jumps to jumps depending on it + pq::PQ # priority queue of next time + lrates::F1 # vector of rate lower bound functions + urates::F1 # vector of rate upper bound functions + rateintervals::F1 # vector of interval length functions + haslratevec::Vector{Bool} # vector of whether an lrate was provided for this vrj + next_delay::Vector{Int} + num_next_delay::Vector{Int} + time_to_next_jump::T + dt_delay::T + vartojumps_map::Union{Nothing, Vector{Vector{Int}}} + dep_gr_delay::Union{Nothing, Dict{Int, Vector{Int}}} +end + +function DelayCoevolveJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::Nothing, + maj::S, rs::F1, affs!::F2, sps::Tuple{Bool, Bool}, + rng::RNG; u::U, dep_graph = nothing, lrates, urates, + rateintervals, haslratevec, dep_graph_delay = nothing, vartojumps_map = nothing,) where {T, S, F1, F2, RNG, U} + if dep_graph === nothing + if (get_num_majumps(maj) == 0) || !isempty(rs) + error("To use DelayCoevolve a dependency graph between jumps must be supplied.") + else + dg = make_dependency_graph(length(u), maj) + end + else + # using a Set to ensure that edges are not duplicate + dgsets = [Set{Int}(append!(Int[], jumps, [var])) + for (var, jumps) in enumerate(dep_graph)] + dg = [sort!(collect(i)) for i in dgsets] + end + + num_jumps = get_num_majumps(maj) + length(urates) + + if length(dg) != num_jumps + error("Number of nodes in the dependency graph must be the same as the number of jumps.") + end + + pq = MutableBinaryMinHeap{T}() + + num_specs = length(u) + + nd = Int64[] + nnd = Int64[] + ttnj = zero(et) + dt_delay = zero(et) + if vartojumps_map === nothing + if (get_num_majumps(maj) == 0) || !isempty(rs) + if dep_graph_delay === nothing + @warn "To use ConstantRateJumps with this algorithm: make sure a delay dependency graph is correctly supplied!" + vartojumps_map = repeat([1:length(crs)], num_specs) + end + else + vartojumps_map = var_to_jumps_map(num_specs, maj) + end + end + dep_gr_delay = dep_graph_delay + DelayCoevolveJumpAggregation{T, S, F1, F2, RNG, typeof(dg), + typeof(pq)}(nj, nj, njt, et, crs, sr, maj, rs, affs!, sps, rng, + dg, pq, lrates, urates, rateintervals, haslratevec, nd, nnd, ttnj, dt_delay, vartojumps_map, dep_gr_delay) +end + +# creating the JumpAggregation structure (tuple-based variable jumps) +function aggregate(aggregator::DelayCoevolve, u, p, t, end_time, constant_jumps, + ma_jumps, save_positions, rng; dep_graph = nothing, + variable_jumps = nothing, dep_graph_delay = nothing, vartojumps_map = nothing, kwargs...) + AffectWrapper = FunctionWrappers.FunctionWrapper{Nothing, Tuple{Any}} + RateWrapper = FunctionWrappers.FunctionWrapper{typeof(t), + Tuple{typeof(u), typeof(p), typeof(t)}} + + ncrjs = (constant_jumps === nothing) ? 0 : length(constant_jumps) + nvrjs = (variable_jumps === nothing) ? 0 : length(variable_jumps) + nrjs = ncrjs + nvrjs + affects! = Vector{AffectWrapper}(undef, nrjs) + rates = Vector{RateWrapper}(undef, nvrjs) + lrates = similar(rates) + rateintervals = similar(rates) + urates = Vector{RateWrapper}(undef, nrjs) + haslratevec = zeros(Bool, nvrjs) + + idx = 1 + if constant_jumps !== nothing + for crj in constant_jumps + affects![idx] = AffectWrapper(integ -> (crj.affect!(integ); nothing)) + urates[idx] = RateWrapper(crj.rate) + idx += 1 + end + end + + if variable_jumps !== nothing + for (i, vrj) in enumerate(variable_jumps) + affects![idx] = AffectWrapper(integ -> (vrj.affect!(integ); nothing)) + urates[idx] = RateWrapper(vrj.urate) + idx += 1 + rates[i] = RateWrapper(vrj.rate) + rateintervals[i] = RateWrapper(vrj.rateinterval) + haslratevec[i] = haslrate(vrj) + lrates[i] = haslratevec[i] ? RateWrapper(vrj.lrate) : RateWrapper(nullrate) + end + end + + num_jumps = get_num_majumps(ma_jumps) + nrjs + cur_rates = Vector{typeof(t)}(undef, num_jumps) + sum_rate = nothing + next_jump = 0 + next_jump_time = typemax(t) + DelayCoevolveJumpAggregation(next_jump, next_jump_time, end_time, cur_rates, sum_rate, + ma_jumps, rates, affects!, save_positions, rng; + u, dep_graph, lrates, urates, rateintervals, haslratevec, dep_graph_delay, vartojumps_map) +end + +# set up a new simulation and calculate the first jump / jump time +function initialize!(p::DelayCoevolveJumpAggregation, integrator, u, params, t) + p.end_time = integrator.sol.prob.tspan[2] + fill_rates_and_get_times!(p, u, params, t) + if p.dep_gr_delay === nothing + p.dep_gr_delay = dep_gr_delay(integrator.delayjumpsets, p.vartojumps_map, length(p.cur_rates)) + end + find_next_delay_dt!(p, integrator) + generate_jumps!(p, integrator, u, params, t) + nothing +end + +# execute one jump, changing the system state +function execute_jumps!(p::DelayCoevolveJumpAggregation, integrator, u, params, t) + # execute jump + update_state_delay!(p, integrator, u, t) + # update current jump rates and times + # update_dependent_rates!(p, u, params, t) + update_dependent_rates_delay!(p, integrator, integrator.u, params, t) + nothing +end + +# calculate the next jump / jump time +function generate_jumps!(p::DelayCoevolveJumpAggregation, integrator, u, params, t) + next_jump_time_reaction, next_jump = top_with_handle(p.pq) + dt_reaction = next_jump_time_reaction - t + dt_delay_generation!(p, integrator) + compare_delay!(p, integrator.de_chan, p.dt_delay, dt_reaction, t) + if !isempty(p.next_delay) + p.next_jump = 0 + else + p.next_jump = next_jump + end + nothing +end + +######################## SSA specific helper routines ######################## +function update_dependent_rates_delay!(p::DelayCoevolveJumpAggregation, integrator, u, params, t) + if isempty(p.next_delay) # if next reaction is not delay reaction + @inbounds deps = p.dep_gr[p.next_jump] + else + # find the dep_rxs w.r.t next_delay vectors + dep_rxs_ = [p.dep_gr_delay[p.next_delay[i]] for i in eachindex(p.next_delay)] + deps = reduce(vcat, dep_rxs_) + end + @unpack cur_rates, end_time, pq = p + for (ix, i) in enumerate(deps) + ti, last_urate_i = next_time(p, u, params, t, i, end_time) + update!(pq, i, ti) + @inbounds cur_rates[i] = last_urate_i + end + nothing +end + +@inline function get_ma_urate(p::DelayCoevolveJumpAggregation, i, u, params, t) + return evalrxrate(u, i, p.ma_jumps) +end + +@inline function get_urate(p::DelayCoevolveJumpAggregation, uidx, u, params, t) + @inbounds return p.urates[uidx](u, params, t) +end + +@inline function get_rateinterval(p::DelayCoevolveJumpAggregation, lidx, u, params, t) + @inbounds return p.rateintervals[lidx](u, params, t) +end + +@inline function get_lrate(p::DelayCoevolveJumpAggregation, lidx, u, params, t) + @inbounds return p.lrates[lidx](u, params, t) +end + +@inline function get_rate(p::DelayCoevolveJumpAggregation, lidx, u, params, t) + @inbounds return p.rates[lidx](u, params, t) +end + +function next_time(p::DelayCoevolveJumpAggregation{T}, u, params, t, i, tstop::T) where {T} + @unpack rng, haslratevec = p + num_majumps = get_num_majumps(p.ma_jumps) + num_cjumps = length(p.urates) - length(p.rates) + uidx = i - num_majumps + lidx = uidx - num_cjumps + urate = uidx > 0 ? get_urate(p, uidx, u, params, t) : get_ma_urate(p, i, u, params, t) + last_urate = p.cur_rates[i] + if i != p.next_jump && last_urate > zero(t) + s = urate == zero(t) ? typemax(t) : last_urate / urate * (p.pq[i] - t) + else + s = urate == zero(t) ? typemax(t) : randexp(rng) / urate + end + _t = t + s + if lidx > 0 + while t < tstop + rateinterval = get_rateinterval(p, lidx, u, params, t) + if s > rateinterval + t = t + rateinterval + urate = get_urate(p, uidx, u, params, t) + s = urate == zero(t) ? typemax(t) : randexp(rng) / urate + _t = t + s + continue + end + (_t >= tstop) && break + + lrate = haslratevec[lidx] ? get_lrate(p, lidx, u, params, t) : zero(t) + if lrate < urate + # when the lower and upper bound are the same, then v < 1 = lrate / urate = urate / urate + v = rand(rng) * urate + # first inequality is less expensive and short-circuits the evaluation + if (v > lrate) && (v > get_rate(p, lidx, u, params, _t)) + t = _t + urate = get_urate(p, uidx, u, params, t) + s = urate == zero(t) ? typemax(t) : randexp(rng) / urate + _t = t + s + continue + end + elseif lrate > urate + error("The lower bound should be lower than the upper bound rate for t = $(t) and i = $(i), but lower bound = $(lrate) > upper bound = $(urate)") + end + break + end + end + return _t, urate +end + +# reevaulate all rates, recalculate all jump times, and reinit the priority queue +function fill_rates_and_get_times!(p::DelayCoevolveJumpAggregation, u, params, t) + @unpack end_time = p + num_jumps = get_num_majumps(p.ma_jumps) + length(p.urates) + p.cur_rates = zeros(typeof(t), num_jumps) + jump_times = Vector{typeof(t)}(undef, num_jumps) + @inbounds for i in 1:num_jumps + jump_times[i], p.cur_rates[i] = next_time(p, u, params, t, i, end_time) + end + p.pq = MutableBinaryMinHeap(jump_times) + nothing +end \ No newline at end of file diff --git a/test/bursty_model.jl b/test/bursty_model.jl index a8f81ad7..504cb4a2 100644 --- a/test/bursty_model.jl +++ b/test/bursty_model.jl @@ -39,7 +39,7 @@ delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) # delayjumpset = DelayJumpSet(delay_trigger, delay_complete, b) -algos = [DelayRejection(), DelayDirect(),DelayMNRM(), DelayDirectCR()] +algos = [DelayRejection(), DelayDirect(),DelayMNRM(), DelayDirectCR(), DelayCoevolve()] timestamps = [10, 20, 50, 200] bursty_mean(t) = a*b*min(t,τ) diff --git a/test/cascade_of_delay_reaction.jl b/test/cascade_of_delay_reaction.jl index 8b3b49ee..1ca6808d 100644 --- a/test/cascade_of_delay_reaction.jl +++ b/test/cascade_of_delay_reaction.jl @@ -42,7 +42,7 @@ dprob = DiscreteProblem(jumpsys, u0, tspan) using Test -algos = [DelayRejection(),DelayDirect(), DelayMNRM(), DelayDirectCR()] +algos = [DelayRejection(),DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] algo = algos[3] djprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, save_positions=(false,false), save_delay_channel= true) # p = djprob.jump_callback.discrete_callbacks[1].affect! diff --git a/test/dep_gr_delay.jl b/test/dep_gr_delay.jl index 8cfb2a2c..b3c8de6e 100644 --- a/test/dep_gr_delay.jl +++ b/test/dep_gr_delay.jl @@ -23,7 +23,7 @@ delay_interrupt = Dict() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) # alg = DelayDirectCR() # alg = DelayMNRM() -for alg = [DelayDirectCR(), DelayMNRM()] +for alg = [DelayDirectCR(), DelayMNRM(), DelayCoevolve()] djprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0) p = djprob.discrete_jump_aggregation; diff --git a/test/low_level_interface.jl b/test/low_level_interface.jl index 0afd73e4..012f3506 100644 --- a/test/low_level_interface.jl +++ b/test/low_level_interface.jl @@ -38,9 +38,29 @@ jumpset_ = JumpSet((), (constant_rate_jump,), nothing, mass_action_jump_) dep_gr_delay = Dict(1=>[4]) dep_gr = [[1,2,3],[1,2,3],[3,4],[4]] de_chan0 = [[]] -algs = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR()] -alg = algs[4] +algs = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] +alg = algs[5] +djprob__ = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=false, dep_graph = dep_gr, dep_graph_delay = dep_gr_delay) +@info "Testing method $(alg)" +sol = solve(djprob__, SSAStepper(), seed =2) +djprob_no_dep_gr_delay = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=false, dep_graph = dep_gr) +@info "Testing method $(alg)" +sol_no_dep_gr_delay = solve(djprob_no_dep_gr_delay, SSAStepper(), seed = 2) +plot(sol) +plot!(sol_no_dep_gr_delay) + +for i in eachindex(sol.u) + @test sol.u[i][3] == length(sol.channel[i][1]) +end + +for i in eachindex(sol.u) + @test sol_no_dep_gr_delay.u[i][3] == length(sol_no_dep_gr_delay.channel[i][1]) +end +for i in eachindex(sol.u) + @test sol_no_dep_gr_delay.u[i] == sol.u[i] + @test sol_no_dep_gr_delay.channel[i] == sol.channel[i] +end for alg in algs djprob__ = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=true, dep_graph = dep_gr, dep_graph_delay = dep_gr_delay) diff --git a/test/save_delay_channel.jl b/test/save_delay_channel.jl index 4267a57e..f9f88934 100644 --- a/test/save_delay_channel.jl +++ b/test/save_delay_channel.jl @@ -26,7 +26,7 @@ u0 = [0, 1, 0] de_chan0 = [[]] tspan = (0.0, tf) dprob = DiscreteProblem(u0, tspan) -algs = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR()] +algs = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] ## TEST From e779e91889c0bac98ad5c68bc18ebaad618713f6 Mon Sep 17 00:00:00 2001 From: xiaoming Date: Thu, 5 Jan 2023 11:34:57 +0100 Subject: [PATCH 04/11] fix issue use urates instead of rs --- src/delayaggregator/delaycoevolve.jl | 6 ++++-- test/low_level_interface.jl | 21 --------------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/src/delayaggregator/delaycoevolve.jl b/src/delayaggregator/delaycoevolve.jl index 4f6b5d9e..d39a7e9b 100644 --- a/src/delayaggregator/delaycoevolve.jl +++ b/src/delayaggregator/delaycoevolve.jl @@ -33,7 +33,7 @@ function DelayCoevolveJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr rng::RNG; u::U, dep_graph = nothing, lrates, urates, rateintervals, haslratevec, dep_graph_delay = nothing, vartojumps_map = nothing,) where {T, S, F1, F2, RNG, U} if dep_graph === nothing - if (get_num_majumps(maj) == 0) || !isempty(rs) + if (get_num_majumps(maj) == 0) || !isempty(urates) error("To use DelayCoevolve a dependency graph between jumps must be supplied.") else dg = make_dependency_graph(length(u), maj) @@ -60,7 +60,9 @@ function DelayCoevolveJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr ttnj = zero(et) dt_delay = zero(et) if vartojumps_map === nothing - if (get_num_majumps(maj) == 0) || !isempty(rs) + # println(dep_graph_delay) + # println(urates) + if (get_num_majumps(maj) == 0) || !isempty(urates) if dep_graph_delay === nothing @warn "To use ConstantRateJumps with this algorithm: make sure a delay dependency graph is correctly supplied!" vartojumps_map = repeat([1:length(crs)], num_specs) diff --git a/test/low_level_interface.jl b/test/low_level_interface.jl index 012f3506..78a9510f 100644 --- a/test/low_level_interface.jl +++ b/test/low_level_interface.jl @@ -39,28 +39,7 @@ dep_gr_delay = Dict(1=>[4]) dep_gr = [[1,2,3],[1,2,3],[3,4],[4]] de_chan0 = [[]] algs = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] -alg = algs[5] -djprob__ = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=false, dep_graph = dep_gr, dep_graph_delay = dep_gr_delay) -@info "Testing method $(alg)" -sol = solve(djprob__, SSAStepper(), seed =2) -djprob_no_dep_gr_delay = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=false, dep_graph = dep_gr) -@info "Testing method $(alg)" -sol_no_dep_gr_delay = solve(djprob_no_dep_gr_delay, SSAStepper(), seed = 2) -plot(sol) -plot!(sol_no_dep_gr_delay) - -for i in eachindex(sol.u) - @test sol.u[i][3] == length(sol.channel[i][1]) -end - -for i in eachindex(sol.u) - @test sol_no_dep_gr_delay.u[i][3] == length(sol_no_dep_gr_delay.channel[i][1]) -end -for i in eachindex(sol.u) - @test sol_no_dep_gr_delay.u[i] == sol.u[i] - @test sol_no_dep_gr_delay.channel[i] == sol.channel[i] -end for alg in algs djprob__ = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=true, dep_graph = dep_gr, dep_graph_delay = dep_gr_delay) From f40962b573874b204b8c1d9cda659bcc5e06882d Mon Sep 17 00:00:00 2001 From: xiaoming Date: Wed, 11 Jan 2023 10:12:39 +0100 Subject: [PATCH 05/11] using SciMLStyle --- docs/make.jl | 66 +++-- examples/bursty.jl | 51 ++-- examples/delay_degradation.jl | 72 ++--- examples/delay_multiple_degradation.jl | 76 ++--- examples/delay_oscillator.jl | 42 +-- examples/heterogeneous_delay.jl | 56 ++-- examples/seir.jl | 30 +- examples/stochastic_delay.jl | 46 +-- src/DelaySSAToolkit.jl | 18 +- src/delaySSA_stepper.jl | 91 +++--- src/delayaggregator/aggregators.jl | 2 +- src/delayaggregator/delaycoevolve.jl | 32 ++- src/delayaggregator/delaydirect.jl | 54 ++-- src/delayaggregator/delaydirectCR.jl | 48 ++-- src/delayaggregator/delaymnrm.jl | 47 ++-- src/delayaggregator/delayrejection.jl | 23 +- src/delayaggregator/delayssajump.jl | 95 ++++--- src/delayproblem.jl | 372 ++++++++++++++----------- src/utils.jl | 41 ++- test/bursty_model.jl | 40 +-- test/cascade_of_delay_reaction.jl | 44 ++- test/check_index_reactions.jl | 9 +- test/delay_problem_test.jl | 49 ++-- test/dep_gr_delay.jl | 46 +-- test/low_level_interface.jl | 33 +-- test/remake_test.jl | 36 +-- test/runtests.jl | 28 +- test/save_delay_channel.jl | 9 +- 28 files changed, 827 insertions(+), 729 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 89b5b54b..a617f47b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,41 +1,39 @@ using DelaySSAToolkit using Documenter -DocMeta.setdocmeta!(DelaySSAToolkit, :DocTestSetup, :(using DelaySSAToolkit); recursive=true) +DocMeta.setdocmeta!(DelaySSAToolkit, :DocTestSetup, :(using DelaySSAToolkit); + recursive = true) makedocs(; - modules=[DelaySSAToolkit], - authors="Xiaoming Fu", - repo="https://github.com/palmtree2013/DelaySSAToolkit.jl/blob/{commit}{path}#{line}", - sitename="DelaySSAToolkit.jl", - format=Documenter.HTML(; - mathengine=Documenter.Writers.HTMLWriter.MathJax2(), - prettyurls=get(ENV, "CI", "false") == "true", - canonical="https://palmtree2013.github.io/DelaySSAToolkit.jl", - assets=String[], - ), - pages=[ - "Home" => "index.md", - "Tutorials" => [ - "tutorials/tutorials.md", - "tutorials/bursty.md", - "tutorials/delay_degradation.md", - "tutorials/heterogeneous_delay.md", - "tutorials/delay_oscillator.md", - "tutorials/stochastic_delay.md", - ], - "Algorithm" => [ - "algorithms/notations.md", - "algorithms/delayrejection.md", - "algorithms/delaydirect.md", - "algorithms/delaymnrm.md", - ], - "Theory" => "theory.md", - "API" => "api.md", - ], -) + modules = [DelaySSAToolkit], + authors = "Xiaoming Fu", + repo = "https://github.com/palmtree2013/DelaySSAToolkit.jl/blob/{commit}{path}#{line}", + sitename = "DelaySSAToolkit.jl", + format = Documenter.HTML(; + mathengine = Documenter.Writers.HTMLWriter.MathJax2(), + prettyurls = get(ENV, "CI", "false") == "true", + canonical = "https://palmtree2013.github.io/DelaySSAToolkit.jl", + assets = String[]), + pages = [ + "Home" => "index.md", + "Tutorials" => [ + "tutorials/tutorials.md", + "tutorials/bursty.md", + "tutorials/delay_degradation.md", + "tutorials/heterogeneous_delay.md", + "tutorials/delay_oscillator.md", + "tutorials/stochastic_delay.md", + ], + "Algorithm" => [ + "algorithms/notations.md", + "algorithms/delayrejection.md", + "algorithms/delaydirect.md", + "algorithms/delaymnrm.md", + ], + "Theory" => "theory.md", + "API" => "api.md", + ]) deploydocs(; - repo="github.com/palmtree2013/DelaySSAToolkit.jl", - devbranch="main", -) + repo = "github.com/palmtree2013/DelaySSAToolkit.jl", + devbranch = "main") diff --git a/examples/bursty.jl b/examples/bursty.jl index 01dadf83..0184fdb3 100644 --- a/examples/bursty.jl +++ b/examples/bursty.jl @@ -9,67 +9,70 @@ begin # construct reaction network @parameters a b t @variables X(t) burst_sup = 30 - rxs = [Reaction(a*b^i/(1+b)^(i+1),nothing,[X],nothing,[i]) for i in 1:burst_sup] + rxs = [Reaction(a * b^i / (1 + b)^(i + 1), nothing, [X], nothing, [i]) + for i in 1:burst_sup] rxs = vcat(rxs) - @named rs = ReactionSystem(rxs,t,[X],[a,b]) + @named rs = ReactionSystem(rxs, t, [X], [a, b]) end # convert the ReactionSystem to a JumpSystem -jumpsys = convert(JumpSystem, rs, combinatoric_ratelaws=false) +jumpsys = convert(JumpSystem, rs, combinatoric_ratelaws = false) u0 = [0] de_chan0 = [[]] -tf = 200. -tspan = (0,tf) +tf = 200.0 +tspan = (0, tf) ps = [0.0282, 3.46] -dprob = DiscreteProblem(jumpsys,u0,tspan,ps) -τ = 130. +dprob = DiscreteProblem(jumpsys, u0, tspan, ps) +τ = 130.0 delay_trigger_affect! = [] for i in 1:burst_sup push!(delay_trigger_affect!, function (integrator, rng) - append!(integrator.de_chan[1], fill(τ, i)) - end) + append!(integrator.de_chan[1], fill(τ, i)) + end) end delay_trigger_affect! delay_trigger = Dict([Pair(i, delay_trigger_affect![i]) for i in 1:burst_sup]) -delay_complete = Dict(1=>[1=>-1]) +delay_complete = Dict(1 => [1 => -1]) delay_interrupt = Dict() # alg = DelayRejection() alg = DelayCoevolve() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) -jprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel = false) +jprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0, + save_positions = (true, true), save_delay_channel = false) # saveat = 0:1:tf seed = 4 @time sol = solve(jprob, SSAStepper(), seed = seed) - ensprob = EnsembleProblem(jprob) -@time ens = solve(ensprob, SSAStepper(), EnsembleSerial(), trajectories=1e5) +@time ens = solve(ensprob, SSAStepper(), EnsembleSerial(), trajectories = 1e5) # Check with the exact probability distribution using TaylorSeries -function taylor_coefficients(NT::Int,at_x,gen::Function) +function taylor_coefficients(NT::Int, at_x, gen::Function) Q = zeros(NT) - taylor_gen = taylor_expand(u->gen(u),at_x,order=NT) + taylor_gen = taylor_expand(u -> gen(u), at_x, order = NT) for j in 1:NT - Q[j] = taylor_gen[j-1] + Q[j] = taylor_gen[j - 1] end return Q end -function delay_bursty(params,NT::Int) +function delay_bursty(params, NT::Int) a, b, τ, t = params - gen1(u) = exp(a*b*min(τ,t)*u/(1-b*u)) - taylor_coefficients(NT,-1,gen1) + gen1(u) = exp(a * b * min(τ, t) * u / (1 - b * u)) + taylor_coefficients(NT, -1, gen1) end - using Catalyst.EnsembleAnalysis -using Plots; theme(:vibrant) +using Plots; +theme(:vibrant); sol_end = componentwise_vectors_timepoint(ens, tf) -histogram(sol_end,bins=0:1:60,normalize=:pdf, label = "Delay SSA",fillalpha = 0.6, linecolor = :orange) +histogram(sol_end, bins = 0:1:60, normalize = :pdf, label = "Delay SSA", fillalpha = 0.6, + linecolor = :orange) -sol_exact = delay_bursty([ps;130;200], 61) -fig = plot!(0:60,sol_exact, linewidth = 3, label = "Exact solution", fmt=:svg, xlabel = "# of products", ylabel = "Probability") +sol_exact = delay_bursty([ps; 130; 200], 61) +fig = plot!(0:60, sol_exact, linewidth = 3, label = "Exact solution", fmt = :svg, + xlabel = "# of products", ylabel = "Probability") # savefig(fig, "docs/src/assets/bursty.svg") diff --git a/examples/delay_degradation.jl b/examples/delay_degradation.jl index fef6b061..75b6089a 100644 --- a/examples/delay_degradation.jl +++ b/examples/delay_degradation.jl @@ -10,75 +10,75 @@ # x_I(t)= 0<=t<=τ ? C*β/(a-γ)*((1-exp(-γ*t))/γ - (1-exp(-a*t))/a) : C*β/a*((1-exp(-γ*τ))/γ + exp(-a*t)*(1-exp(-(a-γ)τ))/(a-γ)) # where a = β + γ - using Catalyst -using DelaySSAToolkit +using DelaySSAToolkit rn = @reaction_network begin - C, 0 --> Xₐ - γ, Xₐ --> 0 - β, Xₐ --> Xᵢ - γ, Xᵢ --> 0 + C, 0 --> Xₐ + γ, Xₐ --> 0 + β, Xₐ --> Xᵢ + γ, Xᵢ --> 0 end C γ β jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) u0 = [0, 0] tf = 30 -saveat = .1 +saveat = 0.1 de_chan0 = [[]] -C, γ, β = [2., 0.1, 0.5] +C, γ, β = [2.0, 0.1, 0.5] p = [C, γ, β] -tspan = (0.,tf) +tspan = (0.0, tf) # aggregatoralgo = DelayRejection() # aggregatoralgo = DelayMNRM() aggregatoralgo = DelayDirect() # aggregatoralgo = DelayDirectCR() dprob = DiscreteProblem(u0, tspan, p) -τ = 15. +τ = 15.0 delay_trigger_affect! = function (integrator, rng) - append!(integrator.de_chan[1], τ) + append!(integrator.de_chan[1], τ) end -delay_trigger = Dict(3=>delay_trigger_affect!) -delay_complete = Dict(1=>[2=>-1]) +delay_trigger = Dict(3 => delay_trigger_affect!) +delay_complete = Dict(1 => [2 => -1]) delay_affect! = function (integrator, rng) i = rand(rng, 1:length(integrator.de_chan[1])) - deleteat!(integrator.de_chan[1],i) + deleteat!(integrator.de_chan[1], i) end -delay_interrupt = Dict(4=>delay_affect!) -delaysets = DelayJumpSet(delay_trigger,delay_complete,delay_interrupt) -djprob = DelayJumpProblem(jumpsys, dprob, aggregatoralgo, delaysets, de_chan0, save_positions = (false, false), save_delay_channel =false) -sol1 =@time solve(djprob, SSAStepper(), seed = 2, saveat =0.1) - - +delay_interrupt = Dict(4 => delay_affect!) +delaysets = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) +djprob = DelayJumpProblem(jumpsys, dprob, aggregatoralgo, delaysets, de_chan0, + save_positions = (false, false), save_delay_channel = false) +sol1 = @time solve(djprob, SSAStepper(), seed = 2, saveat = 0.1) ens_prob = EnsembleProblem(djprob) Sample_size = Int(10^4) -@time ens = solve(ens_prob, SSAStepper(),EnsembleThreads(),trajectories = Sample_size, saveat = .1) +@time ens = solve(ens_prob, SSAStepper(), EnsembleThreads(), trajectories = Sample_size, + saveat = 0.1) - -using Plots, DiffEqBase; theme(:vibrant) -plot(ens[1], label = ["X_A" "X_I"], fmt =:svg) +using Plots, DiffEqBase; +theme(:vibrant); +plot(ens[1], label = ["X_A" "X_I"], fmt = :svg) # savefig("docs/src/assets/delay_degradation1.svg") - -a = β + γ -x_A(t) = C/a*(1-exp(-a*t)) -x_I(t)= 0<=t<=τ ? C*β/(a-γ)*((1-exp(-γ*t))/γ - (1-exp(-a*t))/a) : C*β/a*((1-exp(-γ*τ))/γ + exp(-a*t)*(1-exp((a-γ)τ))/(a-γ)) - +a = β + γ +x_A(t) = C / a * (1 - exp(-a * t)) +function x_I(t) + 0 <= t <= τ ? C * β / (a - γ) * ((1 - exp(-γ * t)) / γ - (1 - exp(-a * t)) / a) : + C * β / a * ((1 - exp(-γ * τ)) / γ + exp(-a * t) * (1 - exp((a - γ)τ)) / (a - γ)) +end using StatsBase using Catalyst.EnsembleAnalysis -tsm(t) = Catalyst.EnsembleAnalysis.timepoint_mean(ens,t) +tsm(t) = Catalyst.EnsembleAnalysis.timepoint_mean(ens, t) mean_A(t) = tsm(t)[1] mean_I(t) = tsm(t)[2] timestamps = 0:0.1:tf -plot(timestamps,x_A.(timestamps),linewidth=3, label = "X_A Exact", xlabel = "Time", ylabel = "# of X_A") -plot!(timestamps,mean_A.(timestamps),linewidth=3,line=:dash, label = "X_A SSA") -plot!(timestamps,x_I.(timestamps),linewidth=3, label = "X_I Exact") -plot!(timestamps,mean_I.(timestamps),linewidth=3,line=:dash, legend = :topleft, label = "X_I SSA") +plot(timestamps, x_A.(timestamps), linewidth = 3, label = "X_A Exact", xlabel = "Time", + ylabel = "# of X_A") +plot!(timestamps, mean_A.(timestamps), linewidth = 3, line = :dash, label = "X_A SSA") +plot!(timestamps, x_I.(timestamps), linewidth = 3, label = "X_I Exact") +plot!(timestamps, mean_I.(timestamps), linewidth = 3, line = :dash, legend = :topleft, + label = "X_I SSA") # savefig("docs/src/assets/delay_degradation2.svg") - - diff --git a/examples/delay_multiple_degradation.jl b/examples/delay_multiple_degradation.jl index fa78e758..5d3a319a 100644 --- a/examples/delay_multiple_degradation.jl +++ b/examples/delay_multiple_degradation.jl @@ -9,65 +9,73 @@ rn = @reaction_network begin β, Xₐ --> Xᵢ₁ + Xᵢ₂ γ, Xᵢ₁ --> 0 γ, Xᵢ₂ --> 0 - end C γ β - jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) - -C, γ, β, τ = [2., 0.1, 0.5, 15.] -p=[C, γ, β] +end C γ β +jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) + +C, γ, β, τ = [2.0, 0.1, 0.5, 15.0] +p = [C, γ, β] u0 = [0, 0, 0] -tf = 30. -saveat = .1 -de_chan0 = [[],[]] -tspan = (0.,tf) -dprob = DiscreteProblem(u0, tspan,p) +tf = 30.0 +saveat = 0.1 +de_chan0 = [[], []] +tspan = (0.0, tf) +dprob = DiscreteProblem(u0, tspan, p) # delay_trigger_affect! = function (integrator, rng) # append!(integrator.de_chan[1], τ) # append!(integrator.de_chan[2], τ) # end # delay_trigger = Dict(3=>delay_trigger_affect!) -delay_trigger = Dict(3=>[1=>τ,2=>τ]) -delay_complete = Dict(1=>[2=>-1],2=>[3=>-1]) +delay_trigger = Dict(3 => [1 => τ, 2 => τ]) +delay_complete = Dict(1 => [2 => -1], 2 => [3 => -1]) delay_affect1! = function (integrator, rng) i = rand(rng, 1:length(integrator.de_chan[1])) - deleteat!(integrator.de_chan[1],i) + deleteat!(integrator.de_chan[1], i) end delay_affect2! = function (integrator, rng) i = rand(rng, 1:length(integrator.de_chan[2])) - deleteat!(integrator.de_chan[2],i) + deleteat!(integrator.de_chan[2], i) end -delay_interrupt = Dict(4=>delay_affect1!,5=>delay_affect2!) -delayjumpset = DelayJumpSet(delay_trigger,delay_complete,delay_interrupt) +delay_interrupt = Dict(4 => delay_affect1!, 5 => delay_affect2!) +delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) -djprob = DelayJumpProblem(jumpsys,dprob, DelayRejection(), delayjumpset, de_chan0, save_positions=(false,false)) -sol =@time solve(djprob, SSAStepper(),seed=10, saveat =.1) -plot(sol, label = [L"X_A" L"X_{I1}" L"X_{I2}"],legend =:topleft,xlabel="time",ylabel="Molecular Number") +djprob = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset, de_chan0, + save_positions = (false, false)) +sol = @time solve(djprob, SSAStepper(), seed = 10, saveat = 0.1) +plot(sol, label = [L"X_A" L"X_{I1}" L"X_{I2}"], legend = :topleft, xlabel = "time", + ylabel = "Molecular Number") timestamps = 0:0.1:tf -a = β + γ -x_A(t) = C/a*(1-exp(-a*t)) -x_I(t)= 0<=t<=τ ? C*β/(a-γ)*((1-exp(-γ*t))/γ - (1-exp(-a*t))/a) : C*β/a*((1-exp(-γ*τ))/γ + exp(-a*t)*(1-exp((a-γ)τ))/(a-γ)) +a = β + γ +x_A(t) = C / a * (1 - exp(-a * t)) +function x_I(t) + 0 <= t <= τ ? C * β / (a - γ) * ((1 - exp(-γ * t)) / γ - (1 - exp(-a * t)) / a) : + C * β / a * ((1 - exp(-γ * τ)) / γ + exp(-a * t) * (1 - exp((a - γ)τ)) / (a - γ)) +end using Plots -plot(timestamps,x_A.(timestamps),linewidth=3,label=L"X_A",xlabel="time",ylabel="Mean Value") -plot!(timestamps,x_I.(timestamps),linewidth=3,label=L"X_I") +plot(timestamps, x_A.(timestamps), linewidth = 3, label = L"X_A", xlabel = "time", + ylabel = "Mean Value") +plot!(timestamps, x_I.(timestamps), linewidth = 3, label = L"X_I") using StatsBase, DifferentialEquations.EnsembleAnalysis Sample_size = Int(1e4) ens_prob = EnsembleProblem(djprob) -ens =@time solve(ens_prob, SSAStepper(),EnsembleThreads(),trajectories = Sample_size, saveat = .1) +ens = @time solve(ens_prob, SSAStepper(), EnsembleThreads(), trajectories = Sample_size, + saveat = 0.1) -tsm(t) = timepoint_mean(ens,t) +tsm(t) = timepoint_mean(ens, t) mean_A(t) = tsm(t)[1] mean_I1(t) = tsm(t)[2] mean_I2(t) = tsm(t)[3] - -plot(timestamps,x_A.(timestamps),linewidth=3,label=L"$X_A$ Exact",xlabel="time",ylabel="Mean Value") -plot!(timestamps,mean_A.(timestamps),linewidth=3,line=:dash,label=L"$X_A$ Delay SSA") -plot!(timestamps,x_I.(timestamps),linewidth=3,label=L"$X_I$ Exact") -plot!(timestamps,mean_I1.(timestamps),linewidth=3,line=:dash,label=L"$X_{I1}$ Delay SSA") -plot!(timestamps,mean_I2.(timestamps),linewidth=3,line=:dash, legend =:topleft,label=L"$X_{I2}$ Delay SSA") - - +plot(timestamps, x_A.(timestamps), linewidth = 3, label = L"$X_A$ Exact", xlabel = "time", + ylabel = "Mean Value") +plot!(timestamps, mean_A.(timestamps), linewidth = 3, line = :dash, + label = L"$X_A$ Delay SSA") +plot!(timestamps, x_I.(timestamps), linewidth = 3, label = L"$X_I$ Exact") +plot!(timestamps, mean_I1.(timestamps), linewidth = 3, line = :dash, + label = L"$X_{I1}$ Delay SSA") +plot!(timestamps, mean_I2.(timestamps), linewidth = 3, line = :dash, legend = :topleft, + label = L"$X_{I2}$ Delay SSA") diff --git a/examples/delay_oscillator.jl b/examples/delay_oscillator.jl index 9db1d294..90f3c99f 100644 --- a/examples/delay_oscillator.jl +++ b/examples/delay_oscillator.jl @@ -6,25 +6,25 @@ using Catalyst # d: X-> 0; k1/(1+Y^2): 0-> X; [trigger X-> Y after τ time;] k2*Y/(1+Y): Y-> 0; rn = @reaction_network begin - 1/(1+Y^2), 0 --> X - 1/(1+Y), Y --> 0 + 1 / (1 + Y^2), 0 --> X + 1 / (1 + Y), Y --> 0 end jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) -u0 = [0,0] +u0 = [0, 0] de_chan0 = [[]] -tf = 400. -tspan = (0,tf) -τ = 20. +tf = 400.0 +tspan = (0, tf) +τ = 20.0 dprob = DiscreteProblem(jumpsys, u0, tspan) # jumpsys.dep_graph delay_trigger_affect! = function (integrator, rng) - append!(integrator.de_chan[1], τ) + append!(integrator.de_chan[1], τ) end -delay_trigger = Dict(1=>delay_trigger_affect!) -delay_complete = Dict(1=>[2=>1, 1=>-1]) +delay_trigger = Dict(1 => delay_trigger_affect!) +delay_complete = Dict(1 => [2 => 1, 1 => -1]) delay_interrupt = Dict() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) # alg = DelayRejection() @@ -34,29 +34,31 @@ alg = DelayDirectCR() djprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0) sol = solve(djprob, SSAStepper(), seed = 12345) using Plots -plot(sol.t,[sol.u[i][1] for i in eachindex(sol.u)],alpha=0.3, label="X",color="green",linewidth = 2, legend = :top, ylabel = "# of individuals", xlabel = "Time", fmt=:svg) +plot(sol.t, [sol.u[i][1] for i in eachindex(sol.u)], alpha = 0.3, label = "X", + color = "green", linewidth = 2, legend = :top, ylabel = "# of individuals", + xlabel = "Time", fmt = :svg) sol = solve(djprob, SSAStepper(), seed = 1234) -plot!(sol.t,[sol.u[i][1] for i in eachindex(sol.u)], label="X",linewidth = 2, line=:dash,color="green") - - +plot!(sol.t, [sol.u[i][1] for i in eachindex(sol.u)], label = "X", linewidth = 2, + line = :dash, color = "green") Sample_size = Int(1e4) ens_prob = EnsembleProblem(djprob) -ens = solve(ens_prob, SSAStepper(), EnsembleThreads(), trajectories = Sample_size, saveat = .1) +ens = solve(ens_prob, SSAStepper(), EnsembleThreads(), trajectories = Sample_size, + saveat = 0.1) using StatsBase -tsm(t) = timepoint_mean(ens,t) +tsm(t) = timepoint_mean(ens, t) mean_X(t) = tsm(t)[1] mean_Y(t) = tsm(t)[2] -timestamps=0:1.0:tf -plot(timestamps,mean_X.(timestamps),linewidth=3,line=:dash,label="X",xlabel="time",ylabel="Mean Value") -plot!(timestamps,mean_Y.(timestamps),linewidth=3,line=:dash, legend = :topright,label="Y") +timestamps = 0:1.0:tf +plot(timestamps, mean_X.(timestamps), linewidth = 3, line = :dash, label = "X", + xlabel = "time", ylabel = "Mean Value") +plot!(timestamps, mean_Y.(timestamps), linewidth = 3, line = :dash, legend = :topright, + label = "Y") # plot(mean_X.(timestamps),mean_Y.(timestamps)) # might be very slow, can try with small end point # @gif for i in 1:200 # plot(mean_X,mean_Y,0,i, linewidth=range(0, 5, length = 200),seriesalpha=range(0, 1, length = 200),xlim=(0,21),ylim=(0,7),label=false,xlabel="X",ylabel="Y") # end - - diff --git a/examples/heterogeneous_delay.jl b/examples/heterogeneous_delay.jl index 41f89aca..4fdc905f 100644 --- a/examples/heterogeneous_delay.jl +++ b/examples/heterogeneous_delay.jl @@ -7,56 +7,56 @@ rn = @reaction_network begin B, Y --> 0 end A B -u0 = [0,0] +u0 = [0, 0] de_chan0 = [[]] -tf = 100. -tspan = (0,tf) -ps = [10., 1.] # dummy variables, later on will be drawn from a Gamma distribution +tf = 100.0 +tspan = (0, tf) +ps = [10.0, 1.0] # dummy variables, later on will be drawn from a Gamma distribution τ = 1 # dummy variables, later on will be drawn from a Gamma distribution -delay_trigger = Dict(1=>[1=>τ]) -delay_complete = Dict(1=>[1=>-1, 2=>1]) +delay_trigger = Dict(1 => [1 => τ]) +delay_complete = Dict(1 => [1 => -1, 2 => 1]) delay_interrupt = Dict() - -jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws=false) -dprob = DiscreteProblem(jumpsys,u0,tspan,ps) +jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) +dprob = DiscreteProblem(jumpsys, u0, tspan, ps) # use ensemble problem algo = DelayRejection() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) -djprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, save_positions=(false,false)) - +djprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, + save_positions = (false, false)) function prob_func1(prob, i, repeat) rng = Random.seed!(i) - A = rand(rng, Gamma(8,1/0.23)) - B = rand(rng, Gamma(9,1/625)) - τ = rand(rng, Gamma(7,1)) - remake(prob, p = [A,B], delay_trigger=Dict(1=>[1=>τ])) + A = rand(rng, Gamma(8, 1 / 0.23)) + B = rand(rng, Gamma(9, 1 / 625)) + τ = rand(rng, Gamma(7, 1)) + remake(prob, p = [A, B], delay_trigger = Dict(1 => [1 => τ])) end function prob_func2(prob, i, repeat) rng = Random.seed!(i) - α = rand(rng, Gamma(63,1/9)) # the Gamma distribution uses shape α, scale θ, Gamma(α,θ). In the paper, Gamma distribution uses shape α and rate β. One needs to set the inverse. - β = rand(rng, Gamma(10.,1/10.)) - A = rand(rng, Gamma(8,1/0.23)) - B = rand(rng, Gamma(9,1/625)) - τ = rand(rng, Gamma(α,1/β)) - remake(prob, p = [A,B], delay_trigger=Dict(1=>[1=>τ])) + α = rand(rng, Gamma(63, 1 / 9)) # the Gamma distribution uses shape α, scale θ, Gamma(α,θ). In the paper, Gamma distribution uses shape α and rate β. One needs to set the inverse. + β = rand(rng, Gamma(10.0, 1 / 10.0)) + A = rand(rng, Gamma(8, 1 / 0.23)) + B = rand(rng, Gamma(9, 1 / 625)) + τ = rand(rng, Gamma(α, 1 / β)) + remake(prob, p = [A, B], delay_trigger = Dict(1 => [1 => τ])) end prob1 = EnsembleProblem(djprob, prob_func = prob_func1) -@time ens1 = solve(prob1, SSAStepper(), EnsembleThreads(), trajectories = 40, saveat= 1.) -Y_evolution = [ens1[i][2,:] for i in 1:40] - +@time ens1 = solve(prob1, SSAStepper(), EnsembleThreads(), trajectories = 40, saveat = 1.0) +Y_evolution = [ens1[i][2, :] for i in 1:40] using DifferentialEquations.EnsembleAnalysis -using Plots;theme(:vibrant) -plot(Y_evolution, linealpha=0.4, legend=false , linewidth = 2, fmt=:svg, xlabel = "Time", ylabel="# of Y individuals") +using Plots; +theme(:vibrant); +plot(Y_evolution, linealpha = 0.4, legend = false, linewidth = 2, fmt = :svg, + xlabel = "Time", ylabel = "# of Y individuals") savefig("docs/src/assets/heterogeneous_delay1.svg") prob2 = EnsembleProblem(djprob, prob_func = prob_func2) @time ens2 = solve(prob2, SSAStepper(), EnsembleThreads(), trajectories = 10^4) last_slice = componentwise_vectors_timepoint(ens2, tf) -histogram(last_slice[1], bins=0:20:1000, normalize=:pdf, xlabel = "# of X individuals", ylabel = "Probability",fillalpha = 0.6, linecolor = :orange, legend = false) +histogram(last_slice[1], bins = 0:20:1000, normalize = :pdf, xlabel = "# of X individuals", + ylabel = "Probability", fillalpha = 0.6, linecolor = :orange, legend = false) # savefig("docs/src/assets/heterogeneous_delay2.svg") - diff --git a/examples/seir.jl b/examples/seir.jl index f0c658b7..fc3a8329 100644 --- a/examples/seir.jl +++ b/examples/seir.jl @@ -1,35 +1,37 @@ using DelaySSAToolkit, Catalyst rn = @reaction_network begin - ρ, S+I --> E+I + ρ, S + I --> E + I r, I --> R end ρ r -jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws=false) +jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) states(jumpsys) -u0 = [999,1,0,0] +u0 = [999, 1, 0, 0] de_chan0 = [[]] -tf = 400. -tspan = (0,tf) - +tf = 400.0 +tspan = (0, tf) ps = [1e-4, 1e-2] -τ = 20. -dprob = DiscreteProblem(jumpsys,u0,tspan, ps) +τ = 20.0 +dprob = DiscreteProblem(jumpsys, u0, tspan, ps) delay_trigger_affect! = function (integrator, rng) append!(integrator.de_chan[1], τ) end -delay_trigger = Dict(1=>delay_trigger_affect!) +delay_trigger = Dict(1 => delay_trigger_affect!) # delay_trigger = Dict(1=>[1=>τ]) -delay_complete = Dict(1=>[2=>1, 3=>-1]) +delay_complete = Dict(1 => [2 => 1, 3 => -1]) delay_interrupt = Dict() # algo = DelayDirect() # algo = DelayDirectCR() algo = DelayRejection() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) -jprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, save_positions=(true,true)) +jprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, + save_positions = (true, true)) @time sol = solve(jprob, SSAStepper(), seed = 1234) -using Plots; theme(:vibrant) -fig = plot(sol, label = ["S" "I" "E" "R"], linewidth = 3, legend = :top, ylabel = "# of individuals", xlabel = "Time", fmt=:png) -savefig(fig,"docs/src/assets/seir.png") \ No newline at end of file +using Plots; +theme(:vibrant); +fig = plot(sol, label = ["S" "I" "E" "R"], linewidth = 3, legend = :top, + ylabel = "# of individuals", xlabel = "Time", fmt = :png) +savefig(fig, "docs/src/assets/seir.png") diff --git a/examples/stochastic_delay.jl b/examples/stochastic_delay.jl index bc36b199..1245c317 100644 --- a/examples/stochastic_delay.jl +++ b/examples/stochastic_delay.jl @@ -8,43 +8,51 @@ rn = @reaction_network begin end kon koff ρ jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) -u0 = [1,0,0] +u0 = [1, 0, 0] de_chan0 = [[]] -tf = 2000. -tspan = (0,tf) -p = [0.0282,0.609,2.11] +tf = 2000.0 +tspan = (0, tf) +p = [0.0282, 0.609, 2.11] dprob = DiscreteProblem(u0, tspan, p) delay_trigger_affect! = function (integrator, rng) - τ=rand(LogNormal(1,sqrt(2)))+120 + τ = rand(LogNormal(1, sqrt(2))) + 120 append!(integrator.de_chan[1], τ) end -delay_trigger = Dict(3=>delay_trigger_affect!) -delay_complete = Dict(1=>[3=>-1]) -delay_interrupt = Dict() -delayjumpset = DelayJumpSet(delay_trigger,delay_complete,delay_interrupt) +delay_trigger = Dict(3 => delay_trigger_affect!) +delay_complete = Dict(1 => [3 => -1]) +delay_interrupt = Dict() +delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) -djprob = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset, de_chan0, save_positions=(false,false)) +djprob = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset, de_chan0, + save_positions = (false, false)) ensprob = EnsembleProblem(djprob) -@time ens = solve(ensprob, SSAStepper(), EnsembleThreads(), trajectories=10^5) +@time ens = solve(ensprob, SSAStepper(), EnsembleThreads(), trajectories = 10^5) -using DifferentialEquations.EnsembleAnalysis, Plots; theme(:vibrant) +using DifferentialEquations.EnsembleAnalysis, Plots; +theme(:vibrant); last_slice = componentwise_vectors_timepoint(ens, tf) -histogram(last_slice[3],bins=0:1:60,normalize=:pdf, label = "LogNormal(1,sqrt(2))",fillalpha = 0.6, linecolor = :orange,fmt=:svg,xlabel = "# of products", ylabel = "Probability") +histogram(last_slice[3], bins = 0:1:60, normalize = :pdf, label = "LogNormal(1,sqrt(2))", + fillalpha = 0.6, linecolor = :orange, fmt = :svg, xlabel = "# of products", + ylabel = "Probability") # savefig("docs/src/assets/stochastic_delay1.svg") delay_trigger_affect2! = function (integrator, rng) - τ=rand(LogNormal(0,2))+120 + τ = rand(LogNormal(0, 2)) + 120 append!(integrator.de_chan[1], τ) end -delay_trigger2 = Dict(3=>delay_trigger_affect2!) -delayjumpset2 = DelayJumpSet(delay_trigger2,delay_complete,delay_interrupt) +delay_trigger2 = Dict(3 => delay_trigger_affect2!) +delayjumpset2 = DelayJumpSet(delay_trigger2, delay_complete, delay_interrupt) -djprob2 = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset2, de_chan0, save_positions=(false,false)) +djprob2 = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset2, de_chan0, + save_positions = (false, false)) -@time ens2 = solve(EnsembleProblem(djprob2), SSAStepper(), EnsembleThreads(), trajectories=10^5) +@time ens2 = solve(EnsembleProblem(djprob2), SSAStepper(), EnsembleThreads(), + trajectories = 10^5) last_slice2 = componentwise_vectors_timepoint(ens2, tf) -histogram(last_slice2[3],bins=0:1:60,normalize=:pdf, label = "LogNormal(0,2)",fillalpha = 0.6, linecolor = :orange,fmt=:svg,xlabel = "# of products", ylabel = "Probability") +histogram(last_slice2[3], bins = 0:1:60, normalize = :pdf, label = "LogNormal(0,2)", + fillalpha = 0.6, linecolor = :orange, fmt = :svg, xlabel = "# of products", + ylabel = "Probability") # savefig("docs/src/assets/stochastic_delay2.svg") diff --git a/src/DelaySSAToolkit.jl b/src/DelaySSAToolkit.jl index bc8cbaa5..05f080c8 100644 --- a/src/DelaySSAToolkit.jl +++ b/src/DelaySSAToolkit.jl @@ -9,26 +9,29 @@ using Base.FastMath: add_fast, sub_fast, exp_fast, log_fast, div_fast import DiffEqBase: DiscreteCallback, init, solve, solve!, initialize! # Types and Structs using JumpProcesses -import JumpProcesses: AbstractJumpAggregator, AbstractJump, JumpProblem, ConstantRateJump, VariableRateJump, RegularJump, MassActionJump, JumpSet, SSAStepper +import JumpProcesses: AbstractJumpAggregator, AbstractJump, JumpProblem, ConstantRateJump, + VariableRateJump, RegularJump, MassActionJump, JumpSet, SSAStepper # Dependency graph functions import JumpProcesses: make_dependency_graph, add_self_dependencies!, var_to_jumps_map # other functions -import JumpProcesses: using_params, get_jump_info_fwrappers, isinplace_jump, extend_problem, build_variable_callback, get_num_majumps +import JumpProcesses: using_params, get_jump_info_fwrappers, isinplace_jump, extend_problem, + build_variable_callback, get_num_majumps # using Catalyst using ModelingToolkit -import ModelingToolkit: JumpSysMajParamMapper, assemble_maj, assemble_vrj, assemble_crj, eqeq_dependencies, variable_dependencies, JumpSystem, states, equations, asgraph, value +import ModelingToolkit: JumpSysMajParamMapper, assemble_maj, assemble_vrj, assemble_crj, + eqeq_dependencies, variable_dependencies, JumpSystem, states, + equations, asgraph, value using DataStructures import DataStructures: update! - using StaticArrays, Base.Threads import Base.Threads @static if VERSION < v"1.3" - seed_multiplier() = Threads.threadid() + seed_multiplier() = Threads.threadid() else - seed_multiplier() = 1 + seed_multiplier() = 1 end include("delayaggregator/aggregators.jl") @@ -43,7 +46,8 @@ include("delaySSA_stepper.jl") include("utils.jl") # export DSSAIntegrator -export DelayJumpProblem, DelayJumpSet, SSAStepper, MassActionJump, ConstantRateJump, JumpProblem, JumpSystem, JumpSet +export DelayJumpProblem, DelayJumpSet, SSAStepper, MassActionJump, ConstantRateJump, + JumpProblem, JumpSystem, JumpSet export DelayRejection, DelayMNRM, DelayDirect, DelayDirectCR, DelayCoevolve export solve, remake diff --git a/src/delaySSA_stepper.jl b/src/delaySSA_stepper.jl index e89b380d..29203d1d 100644 --- a/src/delaySSA_stepper.jl +++ b/src/delaySSA_stepper.jl @@ -1,5 +1,6 @@ -mutable struct DSSAIntegrator{F,uType,tType,P,S,CB,SA,OPT,TS,chanType,chanS} <: DiffEqBase.DEIntegrator{SSAStepper,Nothing,uType,tType} +mutable struct DSSAIntegrator{F, uType, tType, P, S, CB, SA, OPT, TS, chanType, chanS} <: + DiffEqBase.DEIntegrator{SSAStepper, Nothing, uType, tType} f::F u::uType t::tType @@ -24,8 +25,7 @@ mutable struct DSSAIntegrator{F,uType,tType,P,S,CB,SA,OPT,TS,chanType,chanS} <: save_delay_channel::Bool end - -mutable struct DSSASolution{uType,tType,chanS} +mutable struct DSSASolution{uType, tType, chanS} u::uType t::tType channel::chanS @@ -33,19 +33,16 @@ mutable struct DSSASolution{uType,tType,chanS} end function DSSASolution(ode_sol::DiffEqBase.ODESolution, channel) @unpack u, t = ode_sol - DSSASolution{typeof(u),typeof(t),typeof(channel)}(u, t, channel, ode_sol) + DSSASolution{typeof(u), typeof(t), typeof(channel)}(u, t, channel, ode_sol) end - - - function DiffEqBase.u_modified!(integrator::DSSAIntegrator, bool::Bool) integrator.u_modified = bool end function DiffEqBase.__solve(djump_prob::DelayJumpProblem, - alg::SSAStepper; - kwargs...) + alg::SSAStepper; + kwargs...) integrator = init(djump_prob, alg; kwargs...) solve!(integrator) if integrator.save_delay_channel @@ -58,15 +55,15 @@ end ## Initiate the Problem function DiffEqBase.__init(djump_prob::DelayJumpProblem, - alg::SSAStepper; - save_start=true, - save_end=true, - seed=nothing, - alias_jump=Threads.threadid() == 1, - saveat=nothing, - callback=nothing, - tstops=eltype(djump_prob.prob.tspan)[], - numsteps_hint=100) + alg::SSAStepper; + save_start = true, + save_end = true, + seed = nothing, + alias_jump = Threads.threadid() == 1, + saveat = nothing, + callback = nothing, + tstops = eltype(djump_prob.prob.tspan)[], + numsteps_hint = 100) if !(djump_prob.prob isa DiscreteProblem) error("SSAStepper only supports DiscreteProblems.") end @@ -86,7 +83,7 @@ function DiffEqBase.__init(djump_prob::DelayJumpProblem, end end - opts = (callback=CallbackSet(callback),) + opts = (callback = CallbackSet(callback),) prob = djump_prob.prob de_chan0 = convert(Vector{Vector{typeof(prob.tspan[1])}}, djump_prob.de_chan0) if save_start @@ -99,7 +96,9 @@ function DiffEqBase.__init(djump_prob::DelayJumpProblem, chan_sol = typeof(de_chan0)[] # DelaySSA: build chan_sol end - sol = DiffEqBase.build_solution(prob, alg, t, u, dense=false, calculate_error=false, destats=DiffEqBase.DEStats(0), interp=DiffEqBase.ConstantInterpolation(t, u)) + sol = DiffEqBase.build_solution(prob, alg, t, u, dense = false, calculate_error = false, + destats = DiffEqBase.DEStats(0), + interp = DiffEqBase.ConstantInterpolation(t, u)) save_everystep = any(cb.save_positions) if typeof(saveat) <: Number @@ -125,7 +124,11 @@ function DiffEqBase.__init(djump_prob::DelayJumpProblem, sizehint!(t, save_start + save_end) end - integrator = DSSAIntegrator(prob.f, copy(prob.u0), prob.tspan[1], prob.tspan[1], prob.p, sol, 1, prob.tspan[1], cb, _saveat, save_everystep, save_end, cur_saveat, opts, tstops, 1, false, true, deepcopy(de_chan0), chan_sol, djump_prob.delayjumpsets, djump_prob.save_delay_channel) + integrator = DSSAIntegrator(prob.f, copy(prob.u0), prob.tspan[1], prob.tspan[1], prob.p, + sol, 1, prob.tspan[1], cb, _saveat, save_everystep, + save_end, cur_saveat, opts, tstops, 1, false, true, + deepcopy(de_chan0), chan_sol, djump_prob.delayjumpsets, + djump_prob.save_delay_channel) cb.initialize(cb, integrator.u, prob.tspan[1], integrator) DiffEqBase.initialize!(opts.callback, integrator.u, prob.tspan[1], integrator) @@ -134,15 +137,13 @@ end function DiffEqBase.add_tstop!(integrator::DSSAIntegrator, tstop) if tstop > integrator.t - future_tstops = @view integrator.tstops[integrator.tstops_idx:end] + future_tstops = @view integrator.tstops[(integrator.tstops_idx):end] insert_index = integrator.tstops_idx + searchsortedfirst(future_tstops, tstop) - 1 Base.insert!(integrator.tstops, insert_index, tstop) end end - function DiffEqBase.solve!(integrator::DSSAIntegrator) - end_time = integrator.sol.prob.tspan[2] while should_continue_solve(integrator) # It stops before adding a tstop over step!(integrator) @@ -156,13 +157,13 @@ function DiffEqBase.solve!(integrator::DSSAIntegrator) saveat_function!(integrator, last_t) end - if integrator.save_end && (isempty(integrator.sol.t)||integrator.sol.t[end] != end_time) + if integrator.save_end && + (isempty(integrator.sol.t) || integrator.sol.t[end] != end_time) saveat_end_function!(integrator, last_t) end DiffEqBase.finalize!(integrator.opts.callback, integrator.u, integrator.t, integrator) end - function saveat_end_function!(integrator, prev_t) # save last t end_time = integrator.sol.prob.tspan[2] @@ -171,7 +172,8 @@ function saveat_end_function!(integrator, prev_t) # save last u if typeof(integrator.cb.affect!) <: DelayDirectJumpAggregation - update_delay_at_tstop_test!(integrator.cb.affect!, integrator, integrator.p, prev_t, t_final_gap) + update_delay_at_tstop_test!(integrator.cb.affect!, integrator, integrator.p, prev_t, + t_final_gap) end push!(integrator.sol.u, copy(integrator.u)) @@ -195,7 +197,8 @@ function saveat_function!(integrator, prev_t) if integrator.saveat !== nothing && !isempty(integrator.saveat) # Split to help prediction last_saved_t = prev_t - while integrator.cur_saveat < length(integrator.saveat) && (integrator.saveat[integrator.cur_saveat] < integrator.t) + while integrator.cur_saveat < length(integrator.saveat) && + (integrator.saveat[integrator.cur_saveat] < integrator.t) tgap = integrator.saveat[integrator.cur_saveat] - last_saved_t push!(integrator.sol.t, integrator.saveat[integrator.cur_saveat]) push!(integrator.sol.u, copy(integrator.u)) @@ -222,19 +225,21 @@ function saveat_function_direct_method_test!(integrator, prev_t) # save for last step and copied = false changed = false aggregator = integrator.cb.affect! - if (integrator.cur_saveat < length(integrator.saveat)) && (integrator.saveat[integrator.cur_saveat] < integrator.t) + if (integrator.cur_saveat < length(integrator.saveat)) && + (integrator.saveat[integrator.cur_saveat] < integrator.t) shadow_integrator = aggregator.shadow_integrator shadow_integrator.u = copy(integrator.u) shadow_integrator.de_chan = deepcopy(integrator.de_chan) aggregator.copied = true changed = true end - while integrator.cur_saveat < length(integrator.saveat) && (integrator.saveat[integrator.cur_saveat] < integrator.t) - + while integrator.cur_saveat < length(integrator.saveat) && + (integrator.saveat[integrator.cur_saveat] < integrator.t) tgap = integrator.saveat[integrator.cur_saveat] - last_saved_t push!(integrator.sol.t, integrator.saveat[integrator.cur_saveat]) - update_delay_at_tstop_test!(aggregator, shadow_integrator, integrator.p, last_saved_t, tgap) + update_delay_at_tstop_test!(aggregator, shadow_integrator, integrator.p, + last_saved_t, tgap) push!(integrator.sol.u, copy(shadow_integrator.u)) if integrator.save_delay_channel @@ -246,31 +251,33 @@ function saveat_function_direct_method_test!(integrator, prev_t) integrator.cur_saveat += 1 end if aggregator.copied && changed - tend = integrator.t == integrator.sol.prob.tspan[2] ? integrator.sol.prob.tspan[2] : aggregator.next_jump_time + tend = integrator.t == integrator.sol.prob.tspan[2] ? + integrator.sol.prob.tspan[2] : aggregator.next_jump_time last_tgap = tend - last_saved_t - update_delay_at_tstop_test!(aggregator, shadow_integrator, integrator.p, last_saved_t, last_tgap) + update_delay_at_tstop_test!(aggregator, shadow_integrator, integrator.p, + last_saved_t, last_tgap) end end end # The Jump aggregators should not register the next jump through add_tstop! for SSAIntegrator # such that we can achieve maximum performance -@inline function register_next_jump_time!(integrator::DSSAIntegrator, p::AbstractDSSAJumpAggregator, t) +@inline function register_next_jump_time!(integrator::DSSAIntegrator, + p::AbstractDSSAJumpAggregator, t) integrator.tstop = p.next_jump_time nothing end - ## Delay SSA modify function DiffEqBase.step!(integrator::DSSAIntegrator) integrator.tprev = integrator.t - next_jump_time = integrator.tstop > integrator.t ? integrator.tstop : typemax(integrator.tstop) + next_jump_time = integrator.tstop > integrator.t ? integrator.tstop : + typemax(integrator.tstop) doaffect = false if !isempty(integrator.tstops) && integrator.tstops_idx <= length(integrator.tstops) && integrator.tstops[integrator.tstops_idx] < next_jump_time - integrator.t = integrator.tstops[integrator.tstops_idx] integrator.tstops_idx += 1 else @@ -284,7 +291,6 @@ function DiffEqBase.step!(integrator::DSSAIntegrator) saveat_function!(integrator, integrator.tprev) end - # FP error means the new time may equal the old if the next jump time is # sufficiently small, hence we add this check to execute jumps until # this is no longer true. @@ -293,7 +299,8 @@ function DiffEqBase.step!(integrator::DSSAIntegrator) end if !(typeof(integrator.opts.callback.discrete_callbacks) <: Tuple{}) - discrete_modified, saved_in_cb = DiffEqBase.apply_discrete_callback!(integrator, integrator.opts.callback.discrete_callbacks...) + discrete_modified, saved_in_cb = DiffEqBase.apply_discrete_callback!(integrator, + integrator.opts.callback.discrete_callbacks...) else saved_in_cb = false end @@ -303,8 +310,7 @@ function DiffEqBase.step!(integrator::DSSAIntegrator) nothing end - -function DiffEqBase.savevalues!(integrator::DSSAIntegrator, force=false) +function DiffEqBase.savevalues!(integrator::DSSAIntegrator, force = false) saved, savedexactly = false, false # No saveat in here since it would only use previous values, @@ -337,5 +343,4 @@ function should_continue_solve(integrator::DSSAIntegrator) integrator.keep_stepping && (has_jump || has_tstop) end - num_constant_rate_jumps(aggregator::AbstractDSSAJumpAggregator) = length(aggregator.rates) diff --git a/src/delayaggregator/aggregators.jl b/src/delayaggregator/aggregators.jl index 924154b1..f6d5aaf4 100644 --- a/src/delayaggregator/aggregators.jl +++ b/src/delayaggregator/aggregators.jl @@ -55,4 +55,4 @@ needs_vartojumps_map(aggregator::DelayMNRM) = true needs_depgraph(aggregator::DelayDirectCR) = true needs_vartojumps_map(aggregator::DelayDirectCR) = true needs_depgraph(aggregator::DelayCoevolve) = true -needs_vartojumps_map(aggregator::DelayCoevolve) = true \ No newline at end of file +needs_vartojumps_map(aggregator::DelayCoevolve) = true diff --git a/src/delayaggregator/delaycoevolve.jl b/src/delayaggregator/delaycoevolve.jl index d39a7e9b..a3340d23 100644 --- a/src/delayaggregator/delaycoevolve.jl +++ b/src/delayaggregator/delaycoevolve.jl @@ -29,9 +29,10 @@ mutable struct DelayCoevolveJumpAggregation{T, S, F1, F2, RNG, GR, PQ} <: end function DelayCoevolveJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::Nothing, - maj::S, rs::F1, affs!::F2, sps::Tuple{Bool, Bool}, - rng::RNG; u::U, dep_graph = nothing, lrates, urates, - rateintervals, haslratevec, dep_graph_delay = nothing, vartojumps_map = nothing,) where {T, S, F1, F2, RNG, U} + maj::S, rs::F1, affs!::F2, sps::Tuple{Bool, Bool}, + rng::RNG; u::U, dep_graph = nothing, lrates, urates, + rateintervals, haslratevec, dep_graph_delay = nothing, + vartojumps_map = nothing) where {T, S, F1, F2, RNG, U} if dep_graph === nothing if (get_num_majumps(maj) == 0) || !isempty(urates) error("To use DelayCoevolve a dependency graph between jumps must be supplied.") @@ -63,7 +64,7 @@ function DelayCoevolveJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr # println(dep_graph_delay) # println(urates) if (get_num_majumps(maj) == 0) || !isempty(urates) - if dep_graph_delay === nothing + if dep_graph_delay === nothing @warn "To use ConstantRateJumps with this algorithm: make sure a delay dependency graph is correctly supplied!" vartojumps_map = repeat([1:length(crs)], num_specs) end @@ -73,14 +74,18 @@ function DelayCoevolveJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr end dep_gr_delay = dep_graph_delay DelayCoevolveJumpAggregation{T, S, F1, F2, RNG, typeof(dg), - typeof(pq)}(nj, nj, njt, et, crs, sr, maj, rs, affs!, sps, rng, - dg, pq, lrates, urates, rateintervals, haslratevec, nd, nnd, ttnj, dt_delay, vartojumps_map, dep_gr_delay) + typeof(pq)}(nj, nj, njt, et, crs, sr, maj, rs, affs!, sps, + rng, + dg, pq, lrates, urates, rateintervals, + haslratevec, nd, nnd, ttnj, dt_delay, + vartojumps_map, dep_gr_delay) end # creating the JumpAggregation structure (tuple-based variable jumps) function aggregate(aggregator::DelayCoevolve, u, p, t, end_time, constant_jumps, ma_jumps, save_positions, rng; dep_graph = nothing, - variable_jumps = nothing, dep_graph_delay = nothing, vartojumps_map = nothing, kwargs...) + variable_jumps = nothing, dep_graph_delay = nothing, + vartojumps_map = nothing, kwargs...) AffectWrapper = FunctionWrappers.FunctionWrapper{Nothing, Tuple{Any}} RateWrapper = FunctionWrappers.FunctionWrapper{typeof(t), Tuple{typeof(u), typeof(p), typeof(t)}} @@ -122,8 +127,9 @@ function aggregate(aggregator::DelayCoevolve, u, p, t, end_time, constant_jumps, next_jump = 0 next_jump_time = typemax(t) DelayCoevolveJumpAggregation(next_jump, next_jump_time, end_time, cur_rates, sum_rate, - ma_jumps, rates, affects!, save_positions, rng; - u, dep_graph, lrates, urates, rateintervals, haslratevec, dep_graph_delay, vartojumps_map) + ma_jumps, rates, affects!, save_positions, rng; + u, dep_graph, lrates, urates, rateintervals, haslratevec, + dep_graph_delay, vartojumps_map) end # set up a new simulation and calculate the first jump / jump time @@ -131,7 +137,8 @@ function initialize!(p::DelayCoevolveJumpAggregation, integrator, u, params, t) p.end_time = integrator.sol.prob.tspan[2] fill_rates_and_get_times!(p, u, params, t) if p.dep_gr_delay === nothing - p.dep_gr_delay = dep_gr_delay(integrator.delayjumpsets, p.vartojumps_map, length(p.cur_rates)) + p.dep_gr_delay = dep_gr_delay(integrator.delayjumpsets, p.vartojumps_map, + length(p.cur_rates)) end find_next_delay_dt!(p, integrator) generate_jumps!(p, integrator, u, params, t) @@ -163,7 +170,8 @@ function generate_jumps!(p::DelayCoevolveJumpAggregation, integrator, u, params, end ######################## SSA specific helper routines ######################## -function update_dependent_rates_delay!(p::DelayCoevolveJumpAggregation, integrator, u, params, t) +function update_dependent_rates_delay!(p::DelayCoevolveJumpAggregation, integrator, u, + params, t) if isempty(p.next_delay) # if next reaction is not delay reaction @inbounds deps = p.dep_gr[p.next_jump] else @@ -258,4 +266,4 @@ function fill_rates_and_get_times!(p::DelayCoevolveJumpAggregation, u, params, t end p.pq = MutableBinaryMinHeap(jump_times) nothing -end \ No newline at end of file +end diff --git a/src/delayaggregator/delaydirect.jl b/src/delayaggregator/delaydirect.jl index 47da85d7..555934d8 100644 --- a/src/delayaggregator/delaydirect.jl +++ b/src/delayaggregator/delaydirect.jl @@ -1,4 +1,5 @@ -mutable struct DelayDirectJumpAggregation{T,S,F1,F2,RNG,IType} <: AbstractDSSAJumpAggregator +mutable struct DelayDirectJumpAggregation{T, S, F1, F2, RNG, IType} <: + AbstractDSSAJumpAggregator next_jump::Int prev_jump::Int next_jump_time::T @@ -8,7 +9,7 @@ mutable struct DelayDirectJumpAggregation{T,S,F1,F2,RNG,IType} <: AbstractDSSAJu ma_jumps::S rates::F1 affects!::F2 - save_positions::Tuple{Bool,Bool} + save_positions::Tuple{Bool, Bool} rng::RNG next_delay::Vector{Int} num_next_delay::Vector{Int} @@ -17,27 +18,42 @@ mutable struct DelayDirectJumpAggregation{T,S,F1,F2,RNG,IType} <: AbstractDSSAJu shadow_integrator::IType copied::Bool end -mutable struct ShadowIntegrator{uType,chanType,T} +mutable struct ShadowIntegrator{uType, chanType, T} u::uType de_chan::chanType delayjumpsets::DelayJumpSet cur_rates::Vector{T} end -function DelayDirectJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, maj::S, rs::F1, affs!::F2, sps::Tuple{Bool,Bool}, rng::RNG; u0, kwargs...) where {T,S,F1,F2,RNG} +function DelayDirectJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, maj::S, + rs::F1, affs!::F2, sps::Tuple{Bool, Bool}, rng::RNG; u0, + kwargs...) where {T, S, F1, F2, RNG} ttnj = zero(et) ndt = zero(et) - shadow_integrator = ShadowIntegrator{typeof(u0),Vector{Vector{T}}, T}(copy(u0), [Vector{T}()], DelayJumpSet(Dict(),Dict(),Dict()), copy(crs)) + shadow_integrator = ShadowIntegrator{typeof(u0), Vector{Vector{T}}, T}(copy(u0), + [Vector{T}()], + DelayJumpSet(Dict(), + Dict(), + Dict()), + copy(crs)) nd = Int64[] nnd = Int64[] # in the Direct Method the number of next delay equals always 1 - DelayDirectJumpAggregation{T,S,F1,F2,RNG,typeof(shadow_integrator)}(nj, nj, njt, et, crs, sr, maj, rs, affs!, sps, rng, nd, nnd, ttnj, ndt, shadow_integrator, false) + DelayDirectJumpAggregation{T, S, F1, F2, RNG, typeof(shadow_integrator)}(nj, nj, njt, + et, crs, sr, + maj, rs, affs!, + sps, rng, nd, + nnd, ttnj, ndt, + shadow_integrator, + false) end -function aggregate(aggregator::DelayDirect, u, p, t, end_time, constant_jumps, ma_jumps, save_positions, rng; kwargs...) +function aggregate(aggregator::DelayDirect, u, p, t, end_time, constant_jumps, ma_jumps, + save_positions, rng; kwargs...) # handle constant jumps using function wrappers rates, affects! = get_jump_info_fwrappers(u, p, t, constant_jumps) - build_jump_aggregation(DelayDirectJumpAggregation, u, p, t, end_time, ma_jumps, rates, affects!, save_positions, rng; u0=copy(u), kwargs...) + build_jump_aggregation(DelayDirectJumpAggregation, u, p, t, end_time, ma_jumps, rates, + affects!, save_positions, rng; u0 = copy(u), kwargs...) end function initialize!(p::DelayDirectJumpAggregation, integrator, u, params, t) @@ -60,8 +76,8 @@ end """ Create time_to_next_jump based on the shawdow integrator, the goal is to use p.copied to avoid the case when no changes happened wrt integrator.u such that the allocation can be optimised """ -@inline function generate_time_to_next_jump!(p::DelayDirectJumpAggregation, integrator, params, t) - +@inline function generate_time_to_next_jump!(p::DelayDirectJumpAggregation, integrator, + params, t) fill_cum_rates_and_sum!(p, integrator.u, params, t) r1 = rand(p.rng) if isempty(reduce(vcat, integrator.de_chan)) @@ -78,7 +94,7 @@ end cur_T1 += p.next_delay_time if F < r1 # if enter the loop shadow_integrator = p.shadow_integrator - shadow_integrator.u = copy(integrator.u) + shadow_integrator.u = copy(integrator.u) shadow_integrator.de_chan = deepcopy(integrator.de_chan) p.copied = true else @@ -89,7 +105,8 @@ end update_delay_channel!(shadow_integrator.de_chan) update_delay_complete!(p, shadow_integrator) - calculate_sum_rate!(p, shadow_integrator, shadow_integrator.u, params, t + cur_T1) + calculate_sum_rate!(p, shadow_integrator, shadow_integrator.u, params, + t + cur_T1) find_next_delay_num!(p, shadow_integrator.de_chan) prev_T1 = cur_T1 # to avoid cur_T1 = Inf @@ -100,10 +117,10 @@ end F = sub_fast(one(t), exp_fast(-aₜ)) end sum_ = p.copied ? shadow_integrator.cur_rates[end] : p.sum_rate - ttnj_last = div_fast(sub_fast(-log_fast(one(t) - r1), aₜ_), sum_) + ttnj_last = div_fast(sub_fast(-log_fast(one(t) - r1), aₜ_), sum_) ttnj = add_fast(prev_T1, ttnj_last) end - + if p.copied shift_delay_channel!(shadow_integrator.de_chan, ttnj_last) update_delay_channel!(shadow_integrator.de_chan) @@ -113,8 +130,6 @@ end nothing end - - """ update_delay_at_tstop_test! @@ -132,9 +147,9 @@ function update_delay_at_tstop_test!(p, integrator, params, t, tgap) shift_delay_channel!(integrator.de_chan, p.next_delay_time) update_delay_channel!(integrator.de_chan) update_delay_complete!(p, integrator) - find_next_delay_num!(p, integrator.de_chan) + find_next_delay_num!(p, integrator.de_chan) prev_T1 = cur_T1 # to avoid cur_T1 = Inf - cur_T1 += p.next_delay_time + cur_T1 += p.next_delay_time end ttnj_last = sub_fast(tgap, prev_T1) shift_delay_channel!(integrator.de_chan, ttnj_last) @@ -213,7 +228,7 @@ end num_ma_rates = get_num_majumps(ma_jumps) if next_jump <= num_ma_rates # if the next jump is a mass action jump if u isa SVector - integrator.u = executerx(integrator.u, next_jump, ma_jumps) + integrator.u = executerx(integrator.u, next_jump, ma_jumps) else @inbounds executerx!(integrator.u, next_jump, ma_jumps) end @@ -231,4 +246,3 @@ end p.prev_jump = next_jump nothing end - diff --git a/src/delayaggregator/delaydirectCR.jl b/src/delayaggregator/delaydirectCR.jl index 77dc22fe..4f9d2ce5 100644 --- a/src/delayaggregator/delaydirectCR.jl +++ b/src/delayaggregator/delaydirectCR.jl @@ -1,6 +1,8 @@ const MINJUMPRATE = 2.0^exponent(1e-12) -mutable struct DelayDirectCRJumpAggregation{T,S,F1,F2,RNG,DEPGR,U<:JumpProcesses.PriorityTable,W<:Function} <: AbstractDSSAJumpAggregator +mutable struct DelayDirectCRJumpAggregation{T, S, F1, F2, RNG, DEPGR, + U <: JumpProcesses.PriorityTable, W <: Function + } <: AbstractDSSAJumpAggregator next_jump::Int prev_jump::Int next_jump_time::T @@ -10,7 +12,7 @@ mutable struct DelayDirectCRJumpAggregation{T,S,F1,F2,RNG,DEPGR,U<:JumpProcesses ma_jumps::S rates::F1 affects!::F2 - save_positions::Tuple{Bool,Bool} + save_positions::Tuple{Bool, Bool} rng::RNG dep_gr::DEPGR minrate::T @@ -21,15 +23,17 @@ mutable struct DelayDirectCRJumpAggregation{T,S,F1,F2,RNG,DEPGR,U<:JumpProcesses num_next_delay::Vector{Int} time_to_next_jump::T dt_delay::T - vartojumps_map::Union{Nothing,Vector{Vector{Int}}} - dep_gr_delay::Union{Nothing,Dict{Int,Vector{Int}}} + vartojumps_map::Union{Nothing, Vector{Vector{Int}}} + dep_gr_delay::Union{Nothing, Dict{Int, Vector{Int}}} end function DelayDirectCRJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, - maj::S, rs::F1, affs!::F2, sps::Tuple{Bool,Bool}, - rng::RNG; num_specs, dep_graph=nothing, dep_graph_delay=nothing, - minrate=convert(T, MINJUMPRATE), maxrate=convert(T, Inf), vartojumps_map = nothing, - kwargs...) where {T,S,F1,F2,RNG} + maj::S, rs::F1, affs!::F2, sps::Tuple{Bool, Bool}, + rng::RNG; num_specs, dep_graph = nothing, + dep_graph_delay = nothing, + minrate = convert(T, MINJUMPRATE), + maxrate = convert(T, Inf), vartojumps_map = nothing, + kwargs...) where {T, S, F1, F2, RNG} # a dependency graph is needed and must be provided if there are constant rate jumps if dep_graph === nothing @@ -69,33 +73,36 @@ function DelayDirectCRJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr end end dep_gr_delay = dep_graph_delay - DelayDirectCRJumpAggregation{T,S,F1,F2,RNG,typeof(dg),typeof(rt),typeof(ratetogroup)}( - nj, nj, njt, et, crs, sr, maj, rs, affs!, sps, rng, - dg, minrate, maxrate, rt, ratetogroup, nd, nnd, ttnj, dt_delay, vartojumps_map, dep_gr_delay) + DelayDirectCRJumpAggregation{T, S, F1, F2, RNG, typeof(dg), typeof(rt), + typeof(ratetogroup)}(nj, nj, njt, et, crs, sr, maj, rs, + affs!, sps, rng, + dg, minrate, maxrate, rt, ratetogroup, + nd, nnd, ttnj, dt_delay, + vartojumps_map, dep_gr_delay) end - ############################# Required Functions ############################## # creating the JumpAggregation structure (function wrapper-based constant jumps) function aggregate(aggregator::DelayDirectCR, u, p, t, end_time, constant_jumps, - ma_jumps, save_positions, rng; kwargs...) + ma_jumps, save_positions, rng; kwargs...) # handle constant jumps using function wrappers rates, affects! = get_jump_info_fwrappers(u, p, t, constant_jumps) build_jump_aggregation(DelayDirectCRJumpAggregation, u, p, t, end_time, ma_jumps, - rates, affects!, save_positions, rng; num_specs=length(u), kwargs...) + rates, affects!, save_positions, rng; num_specs = length(u), + kwargs...) end - # set up a new simulation and calculate the first jump / jump time function initialize!(p::DelayDirectCRJumpAggregation, integrator, u, params, t) # initialize rates fill_rates_and_sum!(p, u, params, t) if p.dep_gr_delay === nothing - p.dep_gr_delay = dep_gr_delay(integrator.delayjumpsets, p.vartojumps_map, length(p.cur_rates)) + p.dep_gr_delay = dep_gr_delay(integrator.delayjumpsets, p.vartojumps_map, + length(p.cur_rates)) end # setup PriorityTable JumpProcesses.reset!(p.rt) @@ -132,15 +139,12 @@ function generate_jumps!(p::DelayDirectCRJumpAggregation, integrator, u, params, nothing end - - - ######################## SSA specific helper routines ######################### # recalculate jump rates for jumps that depend on the just executed jump # requires dependency graph -function update_dependent_rates_delay!(p::DelayDirectCRJumpAggregation, integrator, u, params, t) - +function update_dependent_rates_delay!(p::DelayDirectCRJumpAggregation, integrator, u, + params, t) if isempty(p.next_delay) # if next reaction is not delay reaction @inbounds dep_rxs = p.dep_gr[p.next_jump] else @@ -164,4 +168,4 @@ function update_dependent_rates_delay!(p::DelayDirectCRJumpAggregation, integrat p.sum_rate = JumpProcesses.groupsum(rt) nothing -end \ No newline at end of file +end diff --git a/src/delayaggregator/delaymnrm.jl b/src/delayaggregator/delaymnrm.jl index 9284f89f..1082427f 100644 --- a/src/delayaggregator/delaymnrm.jl +++ b/src/delayaggregator/delaymnrm.jl @@ -1,4 +1,5 @@ -mutable struct DelayMNRMJumpAggregation{T,S,F1,F2,RNG,DG,PQ} <: AbstractDSSAJumpAggregator +mutable struct DelayMNRMJumpAggregation{T, S, F1, F2, RNG, DG, PQ} <: + AbstractDSSAJumpAggregator next_jump::Int prev_jump::Int next_jump_time::T @@ -8,7 +9,7 @@ mutable struct DelayMNRMJumpAggregation{T,S,F1,F2,RNG,DG,PQ} <: AbstractDSSAJump ma_jumps::S rates::F1 affects!::F2 - save_positions::Tuple{Bool,Bool} + save_positions::Tuple{Bool, Bool} rng::RNG dep_gr::DG pq::PQ @@ -16,12 +17,15 @@ mutable struct DelayMNRMJumpAggregation{T,S,F1,F2,RNG,DG,PQ} <: AbstractDSSAJump num_next_delay::Vector{Int} time_to_next_jump::T dt_delay::T - vartojumps_map::Union{Nothing,Vector{Vector{Int}}} - dep_gr_delay::Union{Nothing,Dict{Int,Vector{Int}}} + vartojumps_map::Union{Nothing, Vector{Vector{Int}}} + dep_gr_delay::Union{Nothing, Dict{Int, Vector{Int}}} end -function DelayMNRMJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, maj::S, rs::F1, affs!::F2, sps::Tuple{Bool,Bool}, rng::RNG; num_specs, dep_graph = nothing, dep_graph_delay = nothing, vartojumps_map = nothing, kwargs...) where {T,S,F1,F2,RNG} - +function DelayMNRMJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, maj::S, + rs::F1, affs!::F2, sps::Tuple{Bool, Bool}, rng::RNG; + num_specs, dep_graph = nothing, dep_graph_delay = nothing, + vartojumps_map = nothing, + kwargs...) where {T, S, F1, F2, RNG} # a dependency graph is needed and must be provided if there are constant rate jumps if dep_graph === nothing @@ -36,7 +40,6 @@ function DelayMNRMJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, add_self_dependencies!(dg) end - pq = MutableBinaryMinHeap{T}() nd = Int64[] @@ -45,7 +48,7 @@ function DelayMNRMJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, dt_delay = zero(et) if vartojumps_map === nothing if (get_num_majumps(maj) == 0) || !isempty(rs) - if dep_graph_delay === nothing + if dep_graph_delay === nothing @warn "To use ConstantRateJumps with the DelayMNRM algorithm: make sure a delay dependency graph is correctly supplied!" vartojumps_map = repeat([1:length(crs)], num_specs) end @@ -54,27 +57,33 @@ function DelayMNRMJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, end end dep_gr_delay = dep_graph_delay - DelayMNRMJumpAggregation{T,S,F1,F2,RNG,typeof(dg),typeof(pq)}(nj, nj, njt, et, crs, sr, maj, rs, affs!, sps, rng, dg, pq, nd, nnd, ttnj, dt_delay, vartojumps_map, dep_gr_delay) + DelayMNRMJumpAggregation{T, S, F1, F2, RNG, typeof(dg), typeof(pq)}(nj, nj, njt, et, + crs, sr, maj, rs, + affs!, sps, rng, dg, + pq, nd, nnd, ttnj, + dt_delay, + vartojumps_map, + dep_gr_delay) end ############################# Required Functions ############################## # creating the JumpAggregation structure (function wrapper-based constant jumps) function aggregate(aggregator::DelayMNRM, u, p, t, end_time, constant_jumps, - ma_jumps, save_positions, rng; kwargs...) + ma_jumps, save_positions, rng; kwargs...) # handle constant jumps using function wrappers rates, affects! = get_jump_info_fwrappers(u, p, t, constant_jumps) build_jump_aggregation(DelayMNRMJumpAggregation, u, p, t, end_time, ma_jumps, - rates, affects!, save_positions, rng; num_specs=length(u), kwargs...) + rates, affects!, save_positions, rng; num_specs = length(u), + kwargs...) end - - # set up a new simulation and calculate the first jump / jump time function initialize!(p::DelayMNRMJumpAggregation, integrator, u, params, t) fill_rates_and_get_times!(p, u, params, t) if p.dep_gr_delay === nothing - p.dep_gr_delay = dep_gr_delay(integrator.delayjumpsets, p.vartojumps_map, length(p.cur_rates)) + p.dep_gr_delay = dep_gr_delay(integrator.delayjumpsets, p.vartojumps_map, + length(p.cur_rates)) end find_next_delay_dt!(p, integrator) generate_jumps!(p, integrator, u, params, t) @@ -105,9 +114,9 @@ function generate_jumps!(p::DelayMNRMJumpAggregation, integrator, u, params, t) nothing end - # recalculate jump rates for jumps that depend on the just executed jump (p.next_jump) -function update_dependent_rates_delay!(p::DelayMNRMJumpAggregation, integrator, u, params, t) +function update_dependent_rates_delay!(p::DelayMNRMJumpAggregation, integrator, u, params, + t) if isempty(p.next_delay) # if next reaction is not delay reaction @inbounds dep_rxs = p.dep_gr[p.next_jump] else @@ -121,12 +130,14 @@ function update_dependent_rates_delay!(p::DelayMNRMJumpAggregation, integrator, oldrate = cur_rates[rx] # update the jump rate - @inbounds cur_rates[rx] = calculate_jump_rate(ma_jumps, num_majumps, rates, u, params, t, rx) + @inbounds cur_rates[rx] = calculate_jump_rate(ma_jumps, num_majumps, rates, u, + params, t, rx) # calculate new jump times for dependent jumps if rx != p.next_jump && oldrate > zero(oldrate) if cur_rates[rx] > zero(eltype(cur_rates)) - DataStructures.update!(p.pq, rx, t + oldrate / cur_rates[rx] * (p.pq[rx] - t)) + DataStructures.update!(p.pq, rx, + t + oldrate / cur_rates[rx] * (p.pq[rx] - t)) else DataStructures.update!(p.pq, rx, typemax(t)) end diff --git a/src/delayaggregator/delayrejection.jl b/src/delayaggregator/delayrejection.jl index 0d1c0471..98d1051f 100644 --- a/src/delayaggregator/delayrejection.jl +++ b/src/delayaggregator/delayrejection.jl @@ -1,4 +1,5 @@ -mutable struct DelayRejectionJumpAggregation{T,S,F1,F2,RNG} <: AbstractDSSAJumpAggregator +mutable struct DelayRejectionJumpAggregation{T, S, F1, F2, RNG} <: + AbstractDSSAJumpAggregator next_jump::Int prev_jump::Int next_jump_time::T @@ -8,7 +9,7 @@ mutable struct DelayRejectionJumpAggregation{T,S,F1,F2,RNG} <: AbstractDSSAJumpA ma_jumps::S rates::F1 affects!::F2 - save_positions::Tuple{Bool,Bool} + save_positions::Tuple{Bool, Bool} rng::RNG next_delay::Vector{Int} num_next_delay::Vector{Int} @@ -16,26 +17,28 @@ mutable struct DelayRejectionJumpAggregation{T,S,F1,F2,RNG} <: AbstractDSSAJumpA dt_delay::T end #3 -function DelayRejectionJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, maj::S, rs::F1, affs!::F2, sps::Tuple{Bool,Bool}, rng::RNG; kwargs...) where {T,S,F1,F2,RNG} +function DelayRejectionJumpAggregation(nj::Int, njt::T, et::T, crs::Vector{T}, sr::T, + maj::S, rs::F1, affs!::F2, sps::Tuple{Bool, Bool}, + rng::RNG; kwargs...) where {T, S, F1, F2, RNG} nd = Int64[] nnd = Int64[] ttnj = zero(et) dt_delay = zero(et) #4 - DelayRejectionJumpAggregation{T,S,F1,F2,RNG}(nj, nj, njt, et, crs, sr, maj, rs, affs!, sps, rng, nd, nnd, ttnj, dt_delay) + DelayRejectionJumpAggregation{T, S, F1, F2, RNG}(nj, nj, njt, et, crs, sr, maj, rs, + affs!, sps, rng, nd, nnd, ttnj, + dt_delay) end #1 function aggregate(aggregator::DelayRejection, u, p, t, end_time, constant_jumps, - ma_jumps, save_positions, rng; kwargs...) - + ma_jumps, save_positions, rng; kwargs...) # handle constant jumps using function wrappers rates, affects! = get_jump_info_fwrappers(u, p, t, constant_jumps) - build_jump_aggregation(DelayRejectionJumpAggregation, u, p, t, end_time, ma_jumps, rates, affects!, save_positions, rng; kwargs...) + build_jump_aggregation(DelayRejectionJumpAggregation, u, p, t, end_time, ma_jumps, + rates, affects!, save_positions, rng; kwargs...) end - - function initialize!(p::DelayRejectionJumpAggregation, integrator, u, params, t) find_next_delay_dt!(p, integrator) generate_jumps!(p, integrator, u, params, t) @@ -88,5 +91,3 @@ function time_to_next_jump!(p::DelayRejectionJumpAggregation, integrator, u, par p.sum_rate = sum_rate nothing end - - diff --git a/src/delayaggregator/delayssajump.jl b/src/delayaggregator/delayssajump.jl index 91387324..2d03f469 100644 --- a/src/delayaggregator/delayssajump.jl +++ b/src/delayaggregator/delayssajump.jl @@ -22,7 +22,9 @@ An aggregator interface for Delay SSA-like algorithms. """ abstract type AbstractDSSAJumpAggregator <: AbstractJumpAggregator end -DiscreteCallback(c::AbstractDSSAJumpAggregator) = DiscreteCallback(c, c, initialize = c, save_positions = c.save_positions) +function DiscreteCallback(c::AbstractDSSAJumpAggregator) + DiscreteCallback(c, c, initialize = c, save_positions = c.save_positions) +end ########### The following routines are templates for all Delay SSAs ########### ########### Generally they should not need to be overloaded. ########### @@ -50,7 +52,7 @@ end function (p::AbstractDSSAJumpAggregator)(dj, u, t, integrator) # initialize initialize!(p, integrator, u, integrator.p, t) register_next_jump_time!(integrator, p, integrator.t) - u_modified!(integrator,false) + u_modified!(integrator, false) nothing end @@ -61,12 +63,15 @@ end Helper routine for setting up standard fields of DSSA jump aggregations. """ -function build_jump_aggregation(jump_agg_type, u, p, t, end_time, ma_jumps, rates, affects!, save_positions, rng; kwargs...) +function build_jump_aggregation(jump_agg_type, u, p, t, end_time, ma_jumps, rates, affects!, + save_positions, rng; kwargs...) # mass action jumps majumps = ma_jumps if majumps === nothing - majumps = MassActionJump(Vector{typeof(t)}(),Vector{Vector{Pair{Int,eltype(u)}}}(),Vector{Vector{Pair{Int,eltype(u)}}}()) + majumps = MassActionJump(Vector{typeof(t)}(), + Vector{Vector{Pair{Int, eltype(u)}}}(), + Vector{Vector{Pair{Int, eltype(u)}}}()) end # current jump rates, allows mass action rates and constant jumps @@ -76,10 +81,9 @@ function build_jump_aggregation(jump_agg_type, u, p, t, end_time, ma_jumps, rate next_jump = 0 next_jump_time = typemax(typeof(t)) jump_agg_type(next_jump, next_jump_time, end_time, cur_rates, sum_rate, - majumps, rates, affects!, save_positions, rng; kwargs...) + majumps, rates, affects!, save_positions, rng; kwargs...) end - """ fill_rates_and_sum!(p::AbstractDSSAJumpAggregator, u, params, t) Reevaluate all rates and their sum. @@ -88,16 +92,16 @@ function fill_rates_and_sum!(p::AbstractDSSAJumpAggregator, u, params, t) sum_rate = zero(typeof(p.sum_rate)) # mass action jumps - majumps = p.ma_jumps + majumps = p.ma_jumps cur_rates = p.cur_rates @inbounds for i in 1:get_num_majumps(majumps) cur_rates[i] = evalrxrate(u, i, majumps) - sum_rate += cur_rates[i] + sum_rate += cur_rates[i] end # constant rates rates = p.rates - idx = get_num_majumps(majumps) + 1 + idx = get_num_majumps(majumps) + 1 @inbounds for rate in rates cur_rates[idx] = rate(u, params, t) sum_rate += cur_rates[idx] @@ -108,7 +112,6 @@ function fill_rates_and_sum!(p::AbstractDSSAJumpAggregator, u, params, t) nothing end - """ calculate_jump_rate(ma_jumps, rates, u, params, t, rx) Recalculate the rate for the jump with index `rx`. @@ -121,21 +124,26 @@ Recalculate the rate for the jump with index `rx`. end end - -@inline function evalrxrate(speciesvec::AbstractVector{T}, rxidx::S,majump::MassActionJump{U,V,W,X})::R where {T,S,R,U <: AbstractVector{R},V,W,X} +@inline function evalrxrate(speciesvec::AbstractVector{T}, rxidx::S, + majump::MassActionJump{U, V, W, X})::R where {T, S, R, + U <: + AbstractVector{R}, + V, W, X} val = one(T) @inbounds for specstoch in majump.reactant_stoch[rxidx] specpop = speciesvec[specstoch[1]] - val *= specpop - @inbounds for _ = 2:specstoch[2] + val *= specpop + @inbounds for _ in 2:specstoch[2] specpop -= one(specpop) - val *= specpop + val *= specpop end end @inbounds return val * majump.scaled_rates[rxidx] end -@inline function executerx!(speciesvec::AbstractVector{T}, rxidx::S,majump::M) where {T,S,M <: JumpProcesses.AbstractMassActionJump} +@inline function executerx!(speciesvec::AbstractVector{T}, rxidx::S, + majump::M) where {T, S, + M <: JumpProcesses.AbstractMassActionJump} @inbounds net_stoch = majump.net_stoch[rxidx] @inbounds for specstoch in net_stoch speciesvec[specstoch[1]] += specstoch[2] @@ -143,10 +151,13 @@ end nothing end -@inline function executerx(speciesvec::SVector{T}, rxidx::S,majump::M) where {T,S,M <: JumpProcesses.AbstractMassActionJump} +@inline function executerx(speciesvec::SVector{T}, rxidx::S, + majump::M) where {T, S, M <: + JumpProcesses.AbstractMassActionJump} @inbounds net_stoch = majump.net_stoch[rxidx] @inbounds for specstoch in net_stoch - speciesvec = setindex(speciesvec,speciesvec[specstoch[1]]+specstoch[2],specstoch[1]) + speciesvec = setindex(speciesvec, speciesvec[specstoch[1]] + specstoch[2], + specstoch[1]) end speciesvec end @@ -155,7 +166,8 @@ end next_jump = p.next_jump ttnj = p.time_to_next_jump @unpack delay_trigger_set, delay_interrupt_set = integrator.delayjumpsets - if !isempty(p.next_delay) || any(set->next_jump in set, [delay_interrupt_set, delay_trigger_set]) + if !isempty(p.next_delay) || + any(set -> next_jump in set, [delay_interrupt_set, delay_trigger_set]) find_next_delay_dt!(p, integrator) else p.dt_delay -= ttnj @@ -181,7 +193,7 @@ end @inbounds p.affects![idx](integrator) end # shift delay channel ! - shift_delay_channel!(integrator.de_chan,ttnj) #shift delays in all channel + shift_delay_channel!(integrator.de_chan, ttnj) #shift delays in all channel if next_jump in delay_interrupt_set update_delay_interrupt!(p, integrator) elseif next_jump in delay_trigger_set @@ -202,7 +214,7 @@ end Compare dt_delay and dt_reaction. """ function compare_delay!(p::AbstractDSSAJumpAggregator, de_chan, dt_delay, dt_reaction, t) - if dt_reaction < dt_delay + if dt_reaction < dt_delay ttnj = dt_reaction next_delay = Int64[] num_next_delay = Int64[] @@ -222,15 +234,14 @@ end Find the minimal dt_delay in all delay channels. """ function find_next_delay_dt!(p, integrator) - de_chan = integrator.de_chan + de_chan = integrator.de_chan p.dt_delay = find_minimum_dt_delay(de_chan) nothing end - # find the minimum of a vector of vectors function find_minimum_dt_delay(de_chan::Vector{Vector{T}}) where {T} - val_vec = Vector{T}(undef,length(de_chan)) + val_vec = Vector{T}(undef, length(de_chan)) @inbounds for i in eachindex(de_chan) val_vec[i] = isempty(de_chan[i]) ? typemax(T) : minimum(de_chan[i]) end @@ -246,7 +257,6 @@ end # isempty(vec) ? typemax(T) : minimum(vec) # end - function find_next_delay_num!(p, de_chan::Vector{Vector{T}}) where {T} @label restart val = find_minimum_dt_delay(de_chan) @@ -260,7 +270,8 @@ function find_next_delay_num!(p, de_chan::Vector{Vector{T}}) where {T} nothing end -@inline function shift_delay_channel!(de_chan::Vector{Vector{T1}},ttnj::T2) where {T1<:Real,T2<:Real} +@inline function shift_delay_channel!(de_chan::Vector{Vector{T1}}, + ttnj::T2) where {T1 <: Real, T2 <: Real} @inbounds for idx in eachindex(de_chan) for j in eachindex(de_chan[idx]) de_chan[idx][j] -= ttnj @@ -268,9 +279,9 @@ end end end -@inline function update_delay_channel!(de_chan::Vector{Vector{T}}) where {T<:Real} +@inline function update_delay_channel!(de_chan::Vector{Vector{T}}) where {T <: Real} @inbounds for idx in eachindex(de_chan) - filter!(x->x>zero(T), de_chan[idx]) + filter!(x -> x > zero(T), de_chan[idx]) end end """ @@ -295,7 +306,8 @@ end function execute_delay_trigger!(delay_trigger_affect!::Function, p, integrator) delay_trigger_affect!(integrator, p.rng) end -function execute_delay_trigger!(delay_trigger_affect!::Vector{Pair{Int64,T}}, p, integrator) where {T} +function execute_delay_trigger!(delay_trigger_affect!::Vector{Pair{Int64, T}}, p, + integrator) where {T} for (chan_idx, τ) in delay_trigger_affect! append!(integrator.de_chan[chan_idx], τ) end @@ -311,27 +323,29 @@ function update_delay_complete!(p, integrator) @inbounds for j in eachindex(next_delay_vec) next_delay = next_delay_vec[j] num_next_delay = num_next_delay_vec[j] - execute_delay_complete!(delay_complete[next_delay], num_next_delay, integrator, p.rng) + execute_delay_complete!(delay_complete[next_delay], num_next_delay, integrator, + p.rng) end end -function execute_delay_complete!(delay_complete::Vector{Pair{Int64,Int64}}, num_next_delay::Int64, integrator, rng) +function execute_delay_complete!(delay_complete::Vector{Pair{Int64, Int64}}, + num_next_delay::Int64, integrator, rng) @inbounds for (i, ξ) in delay_complete if integrator.u isa SVector - integrator.u = setindex(integrator.u, integrator.u[i] + num_next_delay*ξ, i) + integrator.u = setindex(integrator.u, integrator.u[i] + num_next_delay * ξ, i) # integrator.u = setindex!!(integrator.u, integrator.u[i] + num_next_delay*ξ, i) - else - integrator.u[i] += num_next_delay*ξ + else + integrator.u[i] += num_next_delay * ξ end end end -function execute_delay_complete!(delay_complete::Function, num_next_delay::Int64, integrator, rng) +function execute_delay_complete!(delay_complete::Function, num_next_delay::Int64, + integrator, rng) for _ in 1:num_next_delay delay_complete(integrator, rng) end end - """ function vars_to_jumps_delay(p, vars) input::Vector{Int} species indices @@ -352,17 +366,17 @@ end output::Dict next_delay idx => reactions need to be updated """ function dep_gr_delay(delayjumpsets::DelayJumpSet, vartojumps_map, num_reactions) - dict_ = Dict{Int,Vector{Int}}() + dict_ = Dict{Int, Vector{Int}}() dict_complete = delayjumpsets.delay_complete @inbounds for key in keys(dict_complete) delay_complete_action = dict_complete[key] - if typeof(delay_complete_action)<:Vector{Pair{Int,Int}} + if typeof(delay_complete_action) <: Vector{Pair{Int, Int}} vars = first.(delay_complete_action) jumps = unique(vars_to_jumps_delay(vartojumps_map, vars)) else jumps = vec(1:num_reactions) end - push!(dict_, key=>jumps) + push!(dict_, key => jumps) end dict_ end @@ -396,7 +410,6 @@ function find_next_delay_vec(de_chan::Vector{Vector{T}}, ttnj::T) where {T} return position_indices, num_in_vec end - ## decrepit # """ # function find_num_in_vec(A::Vector{Vector{T}}, position_indices::Vector{Int64}, x::T) @@ -431,4 +444,4 @@ end # position_indices = findall(de_chan->ttnj in de_chan, de_chan) # num_in_vec = find_num_in_vec(de_chan, position_indices, ttnj) # return position_indices, num_in_vec -# end \ No newline at end of file +# end diff --git a/src/delayproblem.jl b/src/delayproblem.jl index 410d2716..39a8ed17 100644 --- a/src/delayproblem.jl +++ b/src/delayproblem.jl @@ -70,50 +70,62 @@ delaysets = DelayJumpSet(delay_trigger,delay_complete,delay_interrupt) ``` """ -mutable struct DelayJumpSet{T1<:Any,T2<:Any,T3<:Function} - """reactions in the Markovian part that trigger the change of the state of the delay channels or/and the state of the reactants upon initiation.""" - delay_trigger::Dict{Int,T1} - """reactions in the Markovian part that change the state of the delay channels or/and the state of the reactants in the middle of on-going delay reactions.""" - delay_complete::Dict{Int,T2} - """reactions that are initiated by delay trigger reactions and change the state of the delay channels or/and the state of the reactants upon completion.""" - delay_interrupt::Dict{Int,T3} - """collection of indices of reactions that can interrupt the delay reactions. of `delay_trigger`.""" - delay_trigger_set::Vector{Int} - """collection of indices of `delay_interrupt`.""" - delay_interrupt_set::Vector{Int} +mutable struct DelayJumpSet{T1 <: Any, T2 <: Any, T3 <: Function} + """reactions in the Markovian part that trigger the change of the state of the delay channels or/and the state of the reactants upon initiation.""" + delay_trigger::Dict{Int, T1} + """reactions in the Markovian part that change the state of the delay channels or/and the state of the reactants in the middle of on-going delay reactions.""" + delay_complete::Dict{Int, T2} + """reactions that are initiated by delay trigger reactions and change the state of the delay channels or/and the state of the reactants upon completion.""" + delay_interrupt::Dict{Int, T3} + """collection of indices of reactions that can interrupt the delay reactions. of `delay_trigger`.""" + delay_trigger_set::Vector{Int} + """collection of indices of `delay_interrupt`.""" + delay_interrupt_set::Vector{Int} end function DelayJumpSet(delay_trigger::Dict, delay_complete::Dict, delay_interrupt::Dict) - delay_trigger_, delay_complete_, delay_interrupt_ = convert_delayset.([delay_trigger, delay_complete, delay_interrupt]) - DelayJumpSet(delay_trigger_, delay_complete_, delay_interrupt_, collect(keys(delay_trigger_)), collect(keys(delay_interrupt_))) + delay_trigger_, delay_complete_, delay_interrupt_ = convert_delayset.([ + delay_trigger, + delay_complete, + delay_interrupt, + ]) + DelayJumpSet(delay_trigger_, delay_complete_, delay_interrupt_, + collect(keys(delay_trigger_)), collect(keys(delay_interrupt_))) end -convert_delayset(delay_set::Dict{T1, T2}) where {T1,T2} = isempty(delay_set) ? convert(Dict{Int,Function}, delay_set) : convert(Dict{Int,T2}, delay_set) +function convert_delayset(delay_set::Dict{T1, T2}) where {T1, T2} + isempty(delay_set) ? convert(Dict{Int, Function}, delay_set) : + convert(Dict{Int, T2}, delay_set) +end #BEGIN DelayJump -mutable struct DelayJumpProblem{iip,P,A,C,J<:Union{Nothing,AbstractJumpAggregator},J2,J3,J4,J5,deType,K} <: DiffEqBase.AbstractJumpProblem{P,J} - prob::P - aggregator::A - discrete_jump_aggregation::J - jump_callback::C - variable_jumps::J2 - regular_jump::J3 - massaction_jump::J4 - delayjumpsets::J5 - de_chan0::deType - save_delay_channel::Bool - kwargs::K +mutable struct DelayJumpProblem{iip, P, A, C, J <: Union{Nothing, AbstractJumpAggregator}, + J2, J3, J4, J5, deType, K} <: + DiffEqBase.AbstractJumpProblem{P, J} + prob::P + aggregator::A + discrete_jump_aggregation::J + jump_callback::C + variable_jumps::J2 + regular_jump::J3 + massaction_jump::J4 + delayjumpsets::J5 + de_chan0::deType + save_delay_channel::Bool + kwargs::K end -function DelayJumpProblem(p::P, a::A, dj::J, jc::C, vj::J2, rj::J3, mj::J4, djs::J5, de_chan0::deType, save_delay_channel::Bool, kwargs::K) where {P,A,J,C,J2,J3,J4,J5,deType,K} - if !(typeof(a) <: AbstractDelayAggregatorAlgorithm) - error("To solve DelayJumpProblem, one has to use one of the delay aggregators.") - end - iip = isinplace_jump(p, rj) - DelayJumpProblem{iip,P,A,C,J,J2,J3,J4,J5,deType,K}(p, a, dj, jc, vj, rj, mj, djs, de_chan0, save_delay_channel, kwargs) +function DelayJumpProblem(p::P, a::A, dj::J, jc::C, vj::J2, rj::J3, mj::J4, djs::J5, + de_chan0::deType, save_delay_channel::Bool, + kwargs::K) where {P, A, J, C, J2, J3, J4, J5, deType, K} + if !(typeof(a) <: AbstractDelayAggregatorAlgorithm) + error("To solve DelayJumpProblem, one has to use one of the delay aggregators.") + end + iip = isinplace_jump(p, rj) + DelayJumpProblem{iip, P, A, C, J, J2, J3, J4, J5, deType, K}(p, a, dj, jc, vj, rj, mj, + djs, de_chan0, + save_delay_channel, kwargs) end - - """ function DelayJumpProblem(prob::DiscreteProblem, aggregator::AbstractDelayAggregatorAlgorithm, jumps::JumpSet, delayjumpset::DelayJumpSet, de_chan0) # Fields @@ -136,52 +148,62 @@ end The initial condition of the delay channel. """ -function DelayJumpProblem(prob, aggregator::AbstractDelayAggregatorAlgorithm, jumps::JumpSet, delayjumpsets::DelayJumpSet, de_chan0; - save_positions=typeof(prob) <: DiffEqBase.AbstractDiscreteProblem ? (false, true) : (true, true), - rng=Xorshifts.Xoroshiro128Star(rand(UInt64)), scale_rates=false, useiszero=true, save_delay_channel=false, callback=nothing, kwargs...) - - # initialize the MassActionJump rate constants with the user parameters - if using_params(jumps.massaction_jump) - rates = jumps.massaction_jump.param_mapper(prob.p) - maj = MassActionJump(rates, jumps.massaction_jump.reactant_stoch, jumps.massaction_jump.net_stoch, - jumps.massaction_jump.param_mapper; scale_rates=scale_rates, useiszero=useiszero, - nocopy=true) - else - maj = jumps.massaction_jump - end - - - ## Constant Rate Handling - t, end_time, u = prob.tspan[1], prob.tspan[2], prob.u0 - if (typeof(jumps.constant_jumps) <: Tuple{}) && (maj === nothing) # check if there are no jumps - disc = nothing - constant_jump_callback = CallbackSet() - else - disc = aggregate(aggregator, u, prob.p, t, end_time, jumps.constant_jumps, maj, save_positions, rng; kwargs...) - constant_jump_callback = DiscreteCallback(disc) - end - - iip = isinplace_jump(prob, jumps.regular_jump) - - ## Variable Rate Handling - if typeof(jumps.variable_jumps) <: Tuple{} - new_prob = prob - variable_jump_callback = CallbackSet() - else - new_prob = extend_problem(prob, jumps) - variable_jump_callback = build_variable_callback(CallbackSet(), 0, jumps.variable_jumps...) - end - callbacks = CallbackSet(constant_jump_callback, variable_jump_callback) - - solkwargs = make_kwarg(; callback) - - DelayJumpProblem{iip,typeof(new_prob),typeof(aggregator),typeof(callbacks), - typeof(disc),typeof(jumps.variable_jumps), - typeof(jumps.regular_jump),typeof(maj),typeof(delayjumpsets),typeof(de_chan0),typeof(solkwargs)}( - new_prob, aggregator, disc, - callbacks, - jumps.variable_jumps, - jumps.regular_jump, maj, delayjumpsets, de_chan0, save_delay_channel, solkwargs) +function DelayJumpProblem(prob, aggregator::AbstractDelayAggregatorAlgorithm, + jumps::JumpSet, delayjumpsets::DelayJumpSet, de_chan0; + save_positions = typeof(prob) <: + DiffEqBase.AbstractDiscreteProblem ? + (false, true) : (true, true), + rng = Xorshifts.Xoroshiro128Star(rand(UInt64)), + scale_rates = false, useiszero = true, save_delay_channel = false, + callback = nothing, kwargs...) + + # initialize the MassActionJump rate constants with the user parameters + if using_params(jumps.massaction_jump) + rates = jumps.massaction_jump.param_mapper(prob.p) + maj = MassActionJump(rates, jumps.massaction_jump.reactant_stoch, + jumps.massaction_jump.net_stoch, + jumps.massaction_jump.param_mapper; scale_rates = scale_rates, + useiszero = useiszero, + nocopy = true) + else + maj = jumps.massaction_jump + end + + ## Constant Rate Handling + t, end_time, u = prob.tspan[1], prob.tspan[2], prob.u0 + if (typeof(jumps.constant_jumps) <: Tuple{}) && (maj === nothing) # check if there are no jumps + disc = nothing + constant_jump_callback = CallbackSet() + else + disc = aggregate(aggregator, u, prob.p, t, end_time, jumps.constant_jumps, maj, + save_positions, rng; kwargs...) + constant_jump_callback = DiscreteCallback(disc) + end + + iip = isinplace_jump(prob, jumps.regular_jump) + + ## Variable Rate Handling + if typeof(jumps.variable_jumps) <: Tuple{} + new_prob = prob + variable_jump_callback = CallbackSet() + else + new_prob = extend_problem(prob, jumps) + variable_jump_callback = build_variable_callback(CallbackSet(), 0, + jumps.variable_jumps...) + end + callbacks = CallbackSet(constant_jump_callback, variable_jump_callback) + + solkwargs = make_kwarg(; callback) + + DelayJumpProblem{iip, typeof(new_prob), typeof(aggregator), typeof(callbacks), + typeof(disc), typeof(jumps.variable_jumps), + typeof(jumps.regular_jump), typeof(maj), typeof(delayjumpsets), + typeof(de_chan0), typeof(solkwargs)}(new_prob, aggregator, disc, + callbacks, + jumps.variable_jumps, + jumps.regular_jump, maj, + delayjumpsets, de_chan0, + save_delay_channel, solkwargs) end make_kwarg(; kwargs...) = kwargs @@ -206,89 +228,99 @@ make_kwarg(; kwargs...) = kwargs The initial condition of the delay channel. """ -function DelayJumpProblem(js::JumpSystem, prob, aggregator, delayjumpset, de_chan0; scale_rates=false, save_delay_channel=false, kwargs...) - statetoid = Dict(value(state) => i for (i, state) in enumerate(states(js))) - eqs = equations(js) - invttype = prob.tspan[1] === nothing ? Float64 : typeof(1 / prob.tspan[2]) - - # handling parameter substition and empty param vecs - p = (prob.p isa DiffEqBase.NullParameters || prob.p === nothing) ? Num[] : prob.p - - majpmapper = ModelingToolkit.JumpSysMajParamMapper(js, p; jseqs=eqs, rateconsttype=invttype) - majs = isempty(eqs.x[1]) ? nothing : assemble_maj(eqs.x[1], statetoid, majpmapper) - crjs = ConstantRateJump[assemble_crj(js, j, statetoid) for j in eqs.x[2]] - vrjs = VariableRateJump[assemble_vrj(js, j, statetoid) for j in eqs.x[3]] - ((prob isa DiscreteProblem) && !isempty(vrjs)) && error("Use continuous problems such as an ODEProblem or a SDEProblem with VariableRateJumps") - jset = JumpSet(Tuple(vrjs), Tuple(crjs), nothing, majs) - - if needs_vartojumps_map(aggregator) || needs_depgraph(aggregator) - jdeps = asgraph(js) - vdeps = variable_dependencies(js) - vtoj = jdeps.badjlist - jtov = vdeps.badjlist - jtoj = needs_depgraph(aggregator) ? eqeq_dependencies(jdeps, vdeps).fadjlist : nothing - dep_graph_delay = dep_gr_delay(delayjumpset, vtoj, length(eqs)) - else - vtoj = nothing - jtov = nothing - jtoj = nothing - dep_graph_delay = nothing - end - - - DelayJumpProblem(prob, aggregator, jset, delayjumpset, de_chan0; save_delay_channel=save_delay_channel, dep_graph=jtoj, vartojumps_map=vtoj, jumptovars_map=jtov, scale_rates=scale_rates, dep_graph_delay=dep_graph_delay, nocopy=true, kwargs...) -end +function DelayJumpProblem(js::JumpSystem, prob, aggregator, delayjumpset, de_chan0; + scale_rates = false, save_delay_channel = false, kwargs...) + statetoid = Dict(value(state) => i for (i, state) in enumerate(states(js))) + eqs = equations(js) + invttype = prob.tspan[1] === nothing ? Float64 : typeof(1 / prob.tspan[2]) + + # handling parameter substition and empty param vecs + p = (prob.p isa DiffEqBase.NullParameters || prob.p === nothing) ? Num[] : prob.p + + majpmapper = ModelingToolkit.JumpSysMajParamMapper(js, p; jseqs = eqs, + rateconsttype = invttype) + majs = isempty(eqs.x[1]) ? nothing : assemble_maj(eqs.x[1], statetoid, majpmapper) + crjs = ConstantRateJump[assemble_crj(js, j, statetoid) for j in eqs.x[2]] + vrjs = VariableRateJump[assemble_vrj(js, j, statetoid) for j in eqs.x[3]] + ((prob isa DiscreteProblem) && !isempty(vrjs)) && + error("Use continuous problems such as an ODEProblem or a SDEProblem with VariableRateJumps") + jset = JumpSet(Tuple(vrjs), Tuple(crjs), nothing, majs) + + if needs_vartojumps_map(aggregator) || needs_depgraph(aggregator) + jdeps = asgraph(js) + vdeps = variable_dependencies(js) + vtoj = jdeps.badjlist + jtov = vdeps.badjlist + jtoj = needs_depgraph(aggregator) ? eqeq_dependencies(jdeps, vdeps).fadjlist : + nothing + dep_graph_delay = dep_gr_delay(delayjumpset, vtoj, length(eqs)) + else + vtoj = nothing + jtov = nothing + jtoj = nothing + dep_graph_delay = nothing + end + DelayJumpProblem(prob, aggregator, jset, delayjumpset, de_chan0; + save_delay_channel = save_delay_channel, dep_graph = jtoj, + vartojumps_map = vtoj, jumptovars_map = jtov, + scale_rates = scale_rates, dep_graph_delay = dep_graph_delay, + nocopy = true, kwargs...) +end """ for remaking """ function DiffEqBase.remake(thing::DelayJumpProblem; kwargs...) - - errmesg = """ - DelayJumpProblems can currently only be remade with new u0, de_chan0, p, tspan, delayjumpsets fields, prob fields. - """ - !issubset(keys(kwargs), ((:u0, :de_chan0, :p, :tspan, :prob)..., propertynames(thing.delayjumpsets)...)) && error(errmesg) - - if :prob ∉ keys(kwargs) - dprob = DiffEqBase.remake(thing.prob; kwargs...) - # if the parameters were changed we must remake the MassActionJump too - if (:p ∈ keys(kwargs)) && JumpProcesses.using_params(thing.massaction_jump) - JumpProcesses.update_parameters!(thing.massaction_jump, dprob.p; kwargs...) + errmesg = """ + DelayJumpProblems can currently only be remade with new u0, de_chan0, p, tspan, delayjumpsets fields, prob fields. + """ + !issubset(keys(kwargs), + ((:u0, :de_chan0, :p, :tspan, :prob)..., + propertynames(thing.delayjumpsets)...)) && error(errmesg) + + if :prob ∉ keys(kwargs) + dprob = DiffEqBase.remake(thing.prob; kwargs...) + # if the parameters were changed we must remake the MassActionJump too + if (:p ∈ keys(kwargs)) && JumpProcesses.using_params(thing.massaction_jump) + JumpProcesses.update_parameters!(thing.massaction_jump, dprob.p; kwargs...) + end + else + any(k -> k in keys(kwargs), (:u0, :p, :tspan)) && + error("If remaking a DelayJumpProblem you can not pass both prob and any of u0, p, or tspan.") + dprob = kwargs[:prob] + + # we can't know if p was changed, so we must remake the MassActionJump + if JumpProcesses.using_params(thing.massaction_jump) + JumpProcesses.update_parameters!(thing.massaction_jump, dprob.p; kwargs...) + end end - else - any(k -> k in keys(kwargs), (:u0, :p, :tspan)) && error("If remaking a DelayJumpProblem you can not pass both prob and any of u0, p, or tspan.") - dprob = kwargs[:prob] - - # we can't know if p was changed, so we must remake the MassActionJump - if JumpProcesses.using_params(thing.massaction_jump) - JumpProcesses.update_parameters!(thing.massaction_jump, dprob.p; kwargs...) + if any(k -> k in keys(kwargs), propertynames(thing.delayjumpsets)) + delayjumpsets = update_delayjumpsets(thing.delayjumpsets; kwargs...) + else + delayjumpsets = thing.delayjumpsets end - end - if any(k -> k in keys(kwargs), propertynames(thing.delayjumpsets)) - delayjumpsets = update_delayjumpsets(thing.delayjumpsets; kwargs...) - else - delayjumpsets = thing.delayjumpsets - end - de_chan0 = :de_chan0 ∈ keys(kwargs) ? kwargs[:de_chan0] : thing.de_chan0 - - DelayJumpProblem(dprob, thing.aggregator, thing.discrete_jump_aggregation, thing.jump_callback, - thing.variable_jumps, thing.regular_jump, thing.massaction_jump, delayjumpsets, de_chan0, thing.save_delay_channel, thing.kwargs) + de_chan0 = :de_chan0 ∈ keys(kwargs) ? kwargs[:de_chan0] : thing.de_chan0 + + DelayJumpProblem(dprob, thing.aggregator, thing.discrete_jump_aggregation, + thing.jump_callback, + thing.variable_jumps, thing.regular_jump, thing.massaction_jump, + delayjumpsets, de_chan0, thing.save_delay_channel, thing.kwargs) end function update_delayjumpsets(delayjumpsets::DelayJumpSet; kwargs...) - @unpack delay_trigger, delay_complete, delay_interrupt = delayjumpsets - for (key, value) in kwargs - exp_key = toexpr(key) - if exp_key == :delay_trigger - delay_trigger = value - elseif exp_key == :delay_complete - delay_complete = value - elseif exp_key == :delay_interrupt - delay_interrupt = value + @unpack delay_trigger, delay_complete, delay_interrupt = delayjumpsets + for (key, value) in kwargs + exp_key = toexpr(key) + if exp_key == :delay_trigger + delay_trigger = value + elseif exp_key == :delay_complete + delay_complete = value + elseif exp_key == :delay_interrupt + delay_interrupt = value + end end - end - DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) + DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) end # function update_delayjumpsets(delayjumpsets::DelayJumpSet; kwargs...) @@ -305,20 +337,28 @@ end # return delayjumpsets_ # end -Base.summary(io::IO, prob::DelayJumpProblem) = string(DiffEqBase.parameterless_type(prob), " with problem ", DiffEqBase.parameterless_type(prob.prob), " and aggregator ", typeof(prob.aggregator)) +function Base.summary(io::IO, prob::DelayJumpProblem) + string(DiffEqBase.parameterless_type(prob), " with problem ", + DiffEqBase.parameterless_type(prob.prob), " and aggregator ", + typeof(prob.aggregator)) +end function Base.show(io::IO, mime::MIME"text/plain", A::DelayJumpProblem) - println(io, summary(A)) - println(io, "Number of constant rate jumps: ", A.discrete_jump_aggregation === nothing ? 0 : num_constant_rate_jumps(A.discrete_jump_aggregation)) - println(io, "Number of variable rate jumps: ", length(A.variable_jumps)) - if A.regular_jump !== nothing - println(io, "Have a regular jump") - end - if (A.massaction_jump !== nothing) && (get_num_majumps(A.massaction_jump) > 0) - println(io, "Have a mass action jump") - end - if A.delayjumpsets !== nothing - println(io, "Number of delay trigger reactions: ", length(A.delayjumpsets.delay_trigger)) - println(io, "Number of delay interrupt reactions: ", length(A.delayjumpsets.delay_interrupt)) - end + println(io, summary(A)) + println(io, "Number of constant rate jumps: ", + A.discrete_jump_aggregation === nothing ? 0 : + num_constant_rate_jumps(A.discrete_jump_aggregation)) + println(io, "Number of variable rate jumps: ", length(A.variable_jumps)) + if A.regular_jump !== nothing + println(io, "Have a regular jump") + end + if (A.massaction_jump !== nothing) && (get_num_majumps(A.massaction_jump) > 0) + println(io, "Have a mass action jump") + end + if A.delayjumpsets !== nothing + println(io, "Number of delay trigger reactions: ", + length(A.delayjumpsets.delay_trigger)) + println(io, "Number of delay interrupt reactions: ", + length(A.delayjumpsets.delay_interrupt)) + end end -#END DelayJump \ No newline at end of file +#END DelayJump diff --git a/src/utils.jl b/src/utils.jl index e16df243..9c97c338 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,47 +1,42 @@ - - - Base.@propagate_inbounds Base.getindex(A::DSSASolution, i::Int) = [A.u[i], A.channel[i]] -Base.@propagate_inbounds Base.getindex(A::DSSASolution, i::Int, ::Colon) = [A.u[j][i] for j in eachindex(A.u)] +Base.@propagate_inbounds function Base.getindex(A::DSSASolution, i::Int, ::Colon) + [A.u[j][i] for j in eachindex(A.u)] +end -function (A::DSSASolution)(s::Symbol,i::Int) +function (A::DSSASolution)(s::Symbol, i::Int) if s == :channel @assert i <= length(A.channel[1]) return [A.channel[j][i] for j in eachindex(A.u)] - elseif s ==:u + elseif s == :u @assert i <= length(A.u[1]) return [A.u[j][i] for j in eachindex(A.u)] end -end +end function (A::DSSASolution)(tval) @unpack odesol = A odesol(tval) -end - - +end # (integrator::DSSAIntegrator)(t) = copy(integrator.u) # (integrator::DSSAIntegrator)(out,t) = (out .= integrator.u) - function Base.show(io::IO, m::MIME"text/plain", A::DSSASolution) - println(io,string("retcode: ",A.odesol.retcode)) - println(io,string("Interpolation: "),DiffEqBase.interp_summary(A.odesol.interp)) - print(io,"t: ") - show(io,m,A.t) + println(io, string("retcode: ", A.odesol.retcode)) + println(io, string("Interpolation: "), DiffEqBase.interp_summary(A.odesol.interp)) + print(io, "t: ") + show(io, m, A.t) println(io) - print(io,"u: ") - show(io,m,A.u) + print(io, "u: ") + show(io, m, A.u) println(io) - print(io,"channel: ") - show(io,m,A.channel) - println(io,"\n===\nUse sol.u to check the state variable and sol.channel to check the delay channel solution.\n===") + print(io, "channel: ") + show(io, m, A.channel) + println(io, + "\n===\nUse sol.u to check the state variable and sol.channel to check the delay channel solution.\n===") end - - # """ # function get_reaction_idx(rn::ReactionSystem) @@ -72,4 +67,4 @@ end # # println("The rearranged order :") # vcat([[ModelingToolkit.get_eqs(rn)[order[i]] order[i]] for i in eachindex(order)]...) # end -# export get_reaction_idx \ No newline at end of file +# export get_reaction_idx diff --git a/test/bursty_model.jl b/test/bursty_model.jl index 504cb4a2..1fc5f39c 100644 --- a/test/bursty_model.jl +++ b/test/bursty_model.jl @@ -4,32 +4,31 @@ using Statistics using Test using StaticArrays - reltol = 1e-1 @parameters t @variables X(t) burst_sup = 30 a, b = [0.0282, 3.46] -rxs = [Reaction(a*b^i/(1+b)^(i+1),nothing,[X],nothing,[i]) for i in 1:burst_sup] +rxs = [Reaction(a * b^i / (1 + b)^(i + 1), nothing, [X], nothing, [i]) for i in 1:burst_sup] rxs = vcat(rxs) -@named rs = ReactionSystem(rxs,t,[X],[]) +@named rs = ReactionSystem(rxs, t, [X], []) # convert the ReactionSystem to a JumpSystem -jumpsys = convert(JumpSystem, rs, combinatoric_ratelaws=false) +jumpsys = convert(JumpSystem, rs, combinatoric_ratelaws = false) # equations(jumpsys)|>length u0 = @SVector [0] de_chan0 = [[]] -tf = 200. -tspan = (0,tf) -dprob = DiscreteProblem(jumpsys,u0,tspan) -τ = 130. +tf = 200.0 +tspan = (0, tf) +dprob = DiscreteProblem(jumpsys, u0, tspan) +τ = 130.0 -delay_trigger = Dict([Pair(i, [1=>fill(τ, i)]) for i in 1:burst_sup]) +delay_trigger = Dict([Pair(i, [1 => fill(τ, i)]) for i in 1:burst_sup]) # typeof(delay_trigger[1]) -delay_complete = Dict(1=>[1=>-1]) +delay_complete = Dict(1 => [1 => -1]) delay_interrupt = Dict() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) # a = Dict() @@ -39,24 +38,25 @@ delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) # delayjumpset = DelayJumpSet(delay_trigger, delay_complete, b) -algos = [DelayRejection(), DelayDirect(),DelayMNRM(), DelayDirectCR(), DelayCoevolve()] +algos = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] timestamps = [10, 20, 50, 200] -bursty_mean(t) = a*b*min(t,τ) -bursty_var(t) = 2*a*b^2*min(t,τ) + a*b*min(t,τ) +bursty_mean(t) = a * b * min(t, τ) +bursty_var(t) = 2 * a * b^2 * min(t, τ) + a * b * min(t, τ) # Check mean and variance samplesize = Int64(5e4) @testset for alg in algos - jprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0, save_positions=(false, false)) + jprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0, + save_positions = (false, false)) ensprob = EnsembleProblem(jprob) @info "Testing method $(alg)" - @time ens = solve(ensprob, SSAStepper(), EnsembleSerial(), trajectories=samplesize, saveat = timestamps) + @time ens = solve(ensprob, SSAStepper(), EnsembleSerial(), trajectories = samplesize, + saveat = timestamps) for idx in eachindex(timestamps) - sol_end = reduce(vcat, [ens[i].u[idx+1] for i in 1:samplesize]) - t_ = timestamps[idx] - @test mean(sol_end) ≈ bursty_mean(t_) atol = reltol*bursty_mean(t_) - @test var(sol_end) ≈ bursty_var(t_) atol = reltol*bursty_var(t_) + sol_end = reduce(vcat, [ens[i].u[idx + 1] for i in 1:samplesize]) + t_ = timestamps[idx] + @test mean(sol_end)≈bursty_mean(t_) atol=reltol * bursty_mean(t_) + @test var(sol_end)≈bursty_var(t_) atol=reltol * bursty_var(t_) end end - diff --git a/test/cascade_of_delay_reaction.jl b/test/cascade_of_delay_reaction.jl index 1ca6808d..55e6b3bb 100644 --- a/test/cascade_of_delay_reaction.jl +++ b/test/cascade_of_delay_reaction.jl @@ -1,59 +1,55 @@ using Catalyst, DelaySSAToolkit # 0 -->A1, => A2, ..., => AN => 0 with delay 1 for each cascade delay, N being the length of the delay reaction chain. -rn = @reaction_network begin - 5, 0 --> A1 -end +rn = @reaction_network begin 5, 0 --> A1 end delay_trigger_affect! = [] chain_len = 10 delay_trigger_affect! = function (integrator, rng) - append!(integrator.de_chan[1], 1.) + append!(integrator.de_chan[1], 1.0) end - u0 = zeros(chain_len) de_chan0 = [] for _ in 1:chain_len push!(de_chan0, []) end -tspan = (0.,50.) +tspan = (0.0, 50.0) delay_complete_affect! = [] -for i in 1:chain_len-1 +for i in 1:(chain_len - 1) push!(delay_complete_affect!, function (integrator, rng) - integrator.u[i] -= 1 # A_prev minus 1 - integrator.u[i+1] += 1 # A plus 1 - append!(integrator.de_chan[i+1], 1.) # add to the delay channel - end - ) + integrator.u[i] -= 1 # A_prev minus 1 + integrator.u[i + 1] += 1 # A plus 1 + append!(integrator.de_chan[i + 1], 1.0) # add to the delay channel + end) end push!(delay_complete_affect!, function (integrator, rng) - integrator.u[chain_len] -= 1 # A_prev minus 1 -end) + integrator.u[chain_len] -= 1 # A_prev minus 1 + end) delay_trigger = Dict(Pair(1, delay_trigger_affect!)) -delay_complete = Dict(i=>delay_complete_affect![i] for i in 1:chain_len) +delay_complete = Dict(i => delay_complete_affect![i] for i in 1:chain_len) delay_interrupt = Dict() - delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) dprob = DiscreteProblem(jumpsys, u0, tspan) - using Test -algos = [DelayRejection(),DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] +algos = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] algo = algos[3] -djprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, save_positions=(false,false), save_delay_channel= true) +djprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, + save_positions = (false, false), save_delay_channel = true) # p = djprob.jump_callback.discrete_callbacks[1].affect! # p.dep_gr @testset for algo in algos - djprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, save_positions=(false,false), save_delay_channel= true) + djprob = DelayJumpProblem(jumpsys, dprob, algo, delayjumpset, de_chan0, + save_positions = (false, false), save_delay_channel = true) @info "Testing method $(algo)" - @time sol = solve(djprob, SSAStepper(), saveat = 1.) - for i in 1:length(sol.t)-1, j in 1:chain_len-1 - err = sum(abs2, sol.channel[i][j] .- sol.channel[i+1][j+1]) + @time sol = solve(djprob, SSAStepper(), saveat = 1.0) + for i in 1:(length(sol.t) - 1), j in 1:(chain_len - 1) + err = sum(abs2, sol.channel[i][j] .- sol.channel[i + 1][j + 1]) @test err < 1e-20 - end + end end diff --git a/test/check_index_reactions.jl b/test/check_index_reactions.jl index 50734f97..b464b510 100644 --- a/test/check_index_reactions.jl +++ b/test/check_index_reactions.jl @@ -2,13 +2,12 @@ using Catalyst using Test using DelaySSAToolkit rn = @reaction_network begin - ρ, S+I --> E+I - 1/I, I --> R + ρ, S + I --> E + I + 1 / I, I --> R r, I --> R end ρ r - m = get_reaction_idx(rn) eqs = equations(rn) -@test m[:,2] == [1,3,2] -@test m[:,1] == eqs[[1,3,2]] +@test m[:, 2] == [1, 3, 2] +@test m[:, 1] == eqs[[1, 3, 2]] diff --git a/test/delay_problem_test.jl b/test/delay_problem_test.jl index ad223693..c0161725 100644 --- a/test/delay_problem_test.jl +++ b/test/delay_problem_test.jl @@ -2,58 +2,61 @@ using Test, DelaySSAToolkit, Catalyst ρ, r = [1e-4, 1e-2] rates = [ρ, r] -reactant_stoich = [[1=>1,2=>1],[2=>1]] -net_stoich = [[1=>-1,3=>1],[2=>-1,4=>1]] -mass_jump = MassActionJump(rates, reactant_stoich, net_stoich; scale_rates =false) -jumpset = JumpSet((),(),nothing,mass_jump) - -u0 = [999,1,0,0] # S, I, E, R -tf = 400. -tspan = (0,tf) +reactant_stoich = [[1 => 1, 2 => 1], [2 => 1]] +net_stoich = [[1 => -1, 3 => 1], [2 => -1, 4 => 1]] +mass_jump = MassActionJump(rates, reactant_stoich, net_stoich; scale_rates = false) +jumpset = JumpSet((), (), nothing, mass_jump) + +u0 = [999, 1, 0, 0] # S, I, E, R +tf = 400.0 +tspan = (0, tf) ps = [1e-4, 1e-2] # parameters for ρ, r -τ = 20. -dprob = DiscreteProblem(u0,tspan,ps) +τ = 20.0 +dprob = DiscreteProblem(u0, tspan, ps) delay_trigger_affect! = function (integrator, rng) append!(integrator.de_chan[1], τ) end -delay_trigger = Dict(1=>delay_trigger_affect!) -delay_complete = Dict(1=>[2=>1, 3=>-1]) +delay_trigger = Dict(1 => delay_trigger_affect!) +delay_complete = Dict(1 => [2 => 1, 3 => -1]) delay_interrupt = Dict() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) de_chan0 = [[]] -djprob = DelayJumpProblem(dprob, DelayRejection(), jumpset, delayjumpset, de_chan0, save_positions=(false, false), save_delay_channel = true) -sol = solve(djprob, SSAStepper(), seed = 1, saveat =10.) +djprob = DelayJumpProblem(dprob, DelayRejection(), jumpset, delayjumpset, de_chan0, + save_positions = (false, false), save_delay_channel = true) +sol = solve(djprob, SSAStepper(), seed = 1, saveat = 10.0) io = IOBuffer() show(io, "text/plain", djprob) -@test read(seekstart(io), String) == "\nNumber of constant rate jumps: 0\nNumber of variable rate jumps: 0\nHave a mass action jump\nNumber of delay trigger reactions: 1\nNumber of delay interrupt reactions: 0\n" +@test read(seekstart(io), String) == + "\nNumber of constant rate jumps: 0\nNumber of variable rate jumps: 0\nHave a mass action jump\nNumber of delay trigger reactions: 1\nNumber of delay interrupt reactions: 0\n" sol1 = solve(djprob, SSAStepper(), seed = 1, save_start = false) io1 = IOBuffer() show(io1, "text/plain", sol1) -@test read(seekstart(io1), String) == "retcode: Default\nInterpolation: Piecewise constant interpolation\nt: 1-element Vector{Float64}:\n 400.0\nu: 1-element Vector{Vector{Int64}}:\n [0, 129, 0, 871]\nchannel: 1-element Vector{Vector{Vector{Float64}}}:\n [[]]\n===\nUse sol.u to check the state variable and sol.channel to check the delay channel solution.\n===\n" - +@test read(seekstart(io1), String) == + "retcode: Default\nInterpolation: Piecewise constant interpolation\nt: 1-element Vector{Float64}:\n 400.0\nu: 1-element Vector{Vector{Int64}}:\n [0, 129, 0, 871]\nchannel: 1-element Vector{Vector{Vector{Float64}}}:\n [[]]\n===\nUse sol.u to check the state variable and sol.channel to check the delay channel solution.\n===\n" rn = @reaction_network begin - ρ, S+I --> E+I + ρ, S + I --> E + I r, I --> R end ρ r -jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws=false) -djprob_ = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset, de_chan0, save_positions=(false,false), save_delay_channel = true) +jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) +djprob_ = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset, de_chan0, + save_positions = (false, false), save_delay_channel = true) -sol_ = solve(djprob_, SSAStepper(), seed =1, saveat = 10.) +sol_ = solve(djprob_, SSAStepper(), seed = 1, saveat = 10.0) @testset for i in eachindex(sol.t) @test sol[i] == sol_[i] end @testset for i in 1:3 - @test sol[i,:] == sol_[i,:] + @test sol[i, :] == sol_[i, :] @test sol(:u, i) == sol_(:u, i) end @testset for t in sol.t @test sol(t) == sol_(t) end -@test sol(:channel, 1) == sol_(:channel, 1) \ No newline at end of file +@test sol(:channel, 1) == sol_(:channel, 1) diff --git a/test/dep_gr_delay.jl b/test/dep_gr_delay.jl index b3c8de6e..ca00221a 100644 --- a/test/dep_gr_delay.jl +++ b/test/dep_gr_delay.jl @@ -1,39 +1,39 @@ using DelaySSAToolkit, Catalyst using Test rn = @reaction_network begin - 1/(1+Y^2), 0 --> X - 1/(1+Y), Y --> 0 + 1 / (1 + Y^2), 0 --> X + 1 / (1 + Y), Y --> 0 end jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) # states(rn) -u0 = [0,0] +u0 = [0, 0] de_chan0 = [[]] -tf = 400. -tspan = (0,tf) -τ = 20. +tf = 400.0 +tspan = (0, tf) +τ = 20.0 dprob = DiscreteProblem(jumpsys, u0, tspan) - delay_trigger_affect! = function (integrator, rng) - append!(integrator.de_chan[1], τ) + append!(integrator.de_chan[1], τ) end -delay_trigger = Dict(1=>delay_trigger_affect!) -delay_complete = Dict(1=>[2=>1], 2=>[1=>-1]) +delay_trigger = Dict(1 => delay_trigger_affect!) +delay_complete = Dict(1 => [2 => 1], 2 => [1 => -1]) delay_interrupt = Dict() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) # alg = DelayDirectCR() # alg = DelayMNRM() -for alg = [DelayDirectCR(), DelayMNRM(), DelayCoevolve()] - djprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0) +for alg in [DelayDirectCR(), DelayMNRM(), DelayCoevolve()] + djprob = DelayJumpProblem(jumpsys, dprob, alg, delayjumpset, de_chan0) + + p = djprob.discrete_jump_aggregation + integrator = DelaySSAToolkit.init(djprob, SSAStepper()) + # integrator.de_chan + # delay_complete[1] + # p.dep_gr + # DelaySSAToolkit.dep_gr_delay(p, integrator) - p = djprob.discrete_jump_aggregation; - integrator = DelaySSAToolkit.init(djprob, SSAStepper()) - # integrator.de_chan - # delay_complete[1] - # p.dep_gr - # DelaySSAToolkit.dep_gr_delay(p, integrator) - - @test p.vartojumps_map == [[],[1,2]] - @test p.dep_gr == [[1],[1,2]] - @test DelaySSAToolkit.dep_gr_delay(delayjumpset, p.vartojumps_map, 2) == Dict(1=>[1,2], 2=>[]) -end \ No newline at end of file + @test p.vartojumps_map == [[], [1, 2]] + @test p.dep_gr == [[1], [1, 2]] + @test DelaySSAToolkit.dep_gr_delay(delayjumpset, p.vartojumps_map, 2) == + Dict(1 => [1, 2], 2 => []) +end diff --git a/test/low_level_interface.jl b/test/low_level_interface.jl index 78a9510f..0b026df9 100644 --- a/test/low_level_interface.jl +++ b/test/low_level_interface.jl @@ -2,9 +2,8 @@ using Test, DelaySSAToolkit, Catalyst, StaticArrays params = [0.1, 0.1, 10.0, 0.1, 10.0, 50.0] σ_off, σ_on, ρ_on, d, τ, tf = params -u0_ =@SVector [0,1,0] -tspan = (0., 50.) - +u0_ = @SVector [0, 1, 0] +tspan = (0.0, 50.0) rates_ = [σ_off, σ_on, ρ_on] dprob_ = DiscreteProblem(u0_, tspan, rates_) @@ -18,38 +17,37 @@ delay_interrupt = Dict(4 => delay_affect!) delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) - - rates_ = [σ_off, σ_on, ρ_on] react_stoich_ = [[1 => 1], [2 => 1], [1 => 1]] net_stoich_ = [[1 => -1, 2 => 1], [1 => 1, 2 => -1], [3 => 1]] -mass_action_jump_ = MassActionJump(rates_, react_stoich_, net_stoich_; scale_rates=false) +mass_action_jump_ = MassActionJump(rates_, react_stoich_, net_stoich_; scale_rates = false) affect! = function (integrator) # integrator.u[3] -= 1 integrator.u = setindex(integrator.u, integrator.u[3] - 1, 3) end -rate2 = (u,p,t) -> 0.1*u[3] +rate2 = (u, p, t) -> 0.1 * u[3] constant_rate_jump = ConstantRateJump(rate2, affect!) jumpset_ = JumpSet((), (constant_rate_jump,), nothing, mass_action_jump_) - - -dep_gr_delay = Dict(1=>[4]) -dep_gr = [[1,2,3],[1,2,3],[3,4],[4]] +dep_gr_delay = Dict(1 => [4]) +dep_gr = [[1, 2, 3], [1, 2, 3], [3, 4], [4]] de_chan0 = [[]] algs = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] - for alg in algs - djprob__ = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=true, dep_graph = dep_gr, dep_graph_delay = dep_gr_delay) + djprob__ = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, + save_positions = (true, true), save_delay_channel = true, + dep_graph = dep_gr, dep_graph_delay = dep_gr_delay) @info "Testing method $(alg)" - sol = solve(djprob__, SSAStepper(), seed =1) - - djprob_no_dep_gr_delay = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=true, dep_graph = dep_gr) + sol = solve(djprob__, SSAStepper(), seed = 1) + + djprob_no_dep_gr_delay = DelayJumpProblem(dprob_, alg, jumpset_, delayjumpset, de_chan0, + save_positions = (true, true), + save_delay_channel = true, dep_graph = dep_gr) @info "Testing method $(alg)" sol_no_dep_gr_delay = solve(djprob_no_dep_gr_delay, SSAStepper(), seed = 1) - + for i in eachindex(sol.u) @test sol.u[i][3] == length(sol.channel[i][1]) end @@ -62,4 +60,3 @@ for alg in algs @test sol_no_dep_gr_delay.channel[i] == sol.channel[i] end end - diff --git a/test/remake_test.jl b/test/remake_test.jl index aa0b07a7..f3f97bb4 100644 --- a/test/remake_test.jl +++ b/test/remake_test.jl @@ -1,41 +1,43 @@ using Test, DelaySSAToolkit, Catalyst rn = @reaction_network begin - ρ, S+I --> E+I + ρ, S + I --> E + I r, I --> R end ρ r -u0 = [999,1,0,0] # S, I, E, R -tf = 400. -tspan = (0,tf) +u0 = [999, 1, 0, 0] # S, I, E, R +tf = 400.0 +tspan = (0, tf) ps = [1e-4, 1e-2] # parameters for ρ, r -τ = 20. -dprob = DiscreteProblem(u0,tspan,ps) +τ = 20.0 +dprob = DiscreteProblem(u0, tspan, ps) delay_trigger_affect! = function (integrator, rng) append!(integrator.de_chan[1], τ) end -delay_trigger = Dict(1=>delay_trigger_affect!) -delay_complete = Dict(1=>[2=>1, 3=>-1]) +delay_trigger = Dict(1 => delay_trigger_affect!) +delay_complete = Dict(1 => [2 => 1, 3 => -1]) delay_interrupt = Dict() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) de_chan0 = [[]] -jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws=false) -djprob = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset, de_chan0, save_positions=(false,false), save_delay_channel = true) +jumpsys = convert(JumpSystem, rn, combinatoric_ratelaws = false) +djprob = DelayJumpProblem(jumpsys, dprob, DelayRejection(), delayjumpset, de_chan0, + save_positions = (false, false), save_delay_channel = true) -ps_ = 2*ps +ps_ = 2 * ps de_chan0_ = [[1]] -u0_ = [998,1,1,0] -tspan_ = (0.,200.) +u0_ = [998, 1, 1, 0] +tspan_ = (0.0, 200.0) -delay_trigger_ = Dict(1=>[1=>20.]) -delay_complete_ = Dict(1=>[2=>2, 3=>-2]) +delay_trigger_ = Dict(1 => [1 => 20.0]) +delay_complete_ = Dict(1 => [2 => 2, 3 => -2]) # delay_interrupt_ = Dict() -djprob_ = remake(djprob, p = ps_, de_chan0 = de_chan0_, u0= u0_, tspan = tspan_, delay_trigger = delay_trigger_, delay_complete = delay_complete_) +djprob_ = remake(djprob, p = ps_, de_chan0 = de_chan0_, u0 = u0_, tspan = tspan_, + delay_trigger = delay_trigger_, delay_complete = delay_complete_) @test djprob_.prob.p == ps_ @test djprob_.prob.u0 == u0_ @test djprob_.prob.tspan == tspan_ @test djprob_.delayjumpsets.delay_trigger == delay_trigger_ @test djprob_.delayjumpsets.delay_complete == delay_complete_ -@test djprob_.de_chan0 == de_chan0_ \ No newline at end of file +@test djprob_.de_chan0 == de_chan0_ diff --git a/test/runtests.jl b/test/runtests.jl index c1009289..21b212d0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,25 +2,11 @@ using DelaySSAToolkit using Test, SafeTestsets @time begin - @time @safetestset "Algorithm accuracy" begin - include("bursty_model.jl") - end - @time @safetestset "dep_gr_delay test" begin - include("dep_gr_delay.jl") - end - @time @safetestset "save delay channel test" begin - include("save_delay_channel.jl") - end - @time @safetestset "cascade of delay reaction test" begin - include("cascade_of_delay_reaction.jl") - end - @time @safetestset "delay problem test" begin - include("delay_problem_test.jl") - end - @time @safetestset "remake problem test" begin - include("remake_test.jl") - end - @time @safetestset "low level interface test" begin - include("low_level_interface.jl") - end + @time @safetestset "Algorithm accuracy" begin include("bursty_model.jl") end + @time @safetestset "dep_gr_delay test" begin include("dep_gr_delay.jl") end + @time @safetestset "save delay channel test" begin include("save_delay_channel.jl") end + @time @safetestset "cascade of delay reaction test" begin include("cascade_of_delay_reaction.jl") end + @time @safetestset "delay problem test" begin include("delay_problem_test.jl") end + @time @safetestset "remake problem test" begin include("remake_test.jl") end + @time @safetestset "low level interface test" begin include("low_level_interface.jl") end end diff --git a/test/save_delay_channel.jl b/test/save_delay_channel.jl index f9f88934..bf8e5750 100644 --- a/test/save_delay_channel.jl +++ b/test/save_delay_channel.jl @@ -11,7 +11,7 @@ params = [0.1, 0.1, 10.0, 0.1, 10.0, 50.0] rates = [σ_off, σ_on, ρ_on, d] react_stoich = [[1 => 1], [2 => 1], [1 => 1], [3 => 1]] net_stoich = [[1 => -1, 2 => 1], [1 => 1, 2 => -1], [3 => 1], [3 => -1]] -mass_action_jump = MassActionJump(rates, react_stoich, net_stoich; scale_rates=false) +mass_action_jump = MassActionJump(rates, react_stoich, net_stoich; scale_rates = false) jumpset = JumpSet((), (), nothing, mass_action_jump) delay_trigger = Dict(3 => [1 => τ]) delay_complete = Dict(1 => [3 => -1]) @@ -28,7 +28,6 @@ tspan = (0.0, tf) dprob = DiscreteProblem(u0, tspan) algs = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoevolve()] - ## TEST # alg = algs[3] # djprob = DelayJumpProblem(dprob, alg, jumpset, delayjumpset, de_chan0, save_positions=(false, false), save_delay_channel=true) @@ -45,12 +44,12 @@ algs = [DelayRejection(), DelayDirect(), DelayMNRM(), DelayDirectCR(), DelayCoev # end for alg in algs - djprob = DelayJumpProblem(dprob, alg, jumpset, delayjumpset, de_chan0, save_positions=(true, true), save_delay_channel=true) + djprob = DelayJumpProblem(dprob, alg, jumpset, delayjumpset, de_chan0, + save_positions = (true, true), save_delay_channel = true) @info "Testing method $(alg)" sol = solve(djprob, SSAStepper()) - + for i in eachindex(sol.u) @test sol.u[i][3] == length(sol.channel[i][1]) end end - From 6580cfb3023772b889d734dad7f2efd057919abc Mon Sep 17 00:00:00 2001 From: xiaoming Date: Wed, 11 Jan 2023 10:45:40 +0100 Subject: [PATCH 06/11] Update delayssajump.jl --- src/delayaggregator/delayssajump.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/delayaggregator/delayssajump.jl b/src/delayaggregator/delayssajump.jl index 2d03f469..d8dcc805 100644 --- a/src/delayaggregator/delayssajump.jl +++ b/src/delayaggregator/delayssajump.jl @@ -398,7 +398,7 @@ julia> find_next_delay_vec(A, x) ([1, 2, 3], [2, 1, 1]) ``` """ -function find_next_delay_vec(de_chan::Vector{Vector{T}}, ttnj::T) where {T} +function find_next_delay_vec(de_chan::Vector{Vector{T}}, ttnj::T) where {T<:Real} position_indices = Vector{Int64}() num_in_vec = Vector{Int64}() for idx in eachindex(de_chan) From b9a853724e312829897de12b63a661e610b63cfe Mon Sep 17 00:00:00 2001 From: xiaoming Date: Wed, 11 Jan 2023 10:50:21 +0100 Subject: [PATCH 07/11] add code style --- README.md | 6 +++--- src/delayaggregator/delayssajump.jl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2e5c2cab..c6296461 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ |:-----------------:|:----------------:|:----------------:| | [![doc dev badge]][doc dev link] | [![ci badge]][ci link] [![cov badge]][cov link] | [![download badge]][download link]| --> -| **Documentation** | **Build Status** | -|:-----------------:|:----------------:| -| [![doc dev badge]][doc dev link] | [![ci badge]][ci link] [![cov badge]][cov link] | +| **Documentation** | **Build Status** | **Code Style** | +|:-----------------:|:----------------:|:----------------:| +| [![doc dev badge]][doc dev link] | [![ci badge]][ci link] [![cov badge]][cov link] | [![SciML Code Style](https://img.shields.io/static/v1?label=code%20style&message=SciML&color=9558b2&labelColor=389826)](https://github.com/SciML/SciMLStyle) [doc dev badge]: https://img.shields.io/badge/docs-dev-blue.svg [doc dev link]: https://palmtree2013.github.io/DelaySSAToolkit.jl/dev/ diff --git a/src/delayaggregator/delayssajump.jl b/src/delayaggregator/delayssajump.jl index d8dcc805..40b73820 100644 --- a/src/delayaggregator/delayssajump.jl +++ b/src/delayaggregator/delayssajump.jl @@ -352,8 +352,8 @@ input::Vector{Int} species indices output::Vector{Int} reactions need to be updated """ -function vars_to_jumps_delay(vartojumps_map::Vector{Vector{Int}}, vars::Vector{Int}) - jumps = [] +function vars_to_jumps_delay(vartojumps_map::Vector{Vector{Int}}, vars::Vector{Int})::Vector{Int} + jumps = Vector{Int}[] for var in vars push!(jumps, vartojumps_map[var]) end From ed80ad2bbec51f6b5312e568193f9d42d2e9770f Mon Sep 17 00:00:00 2001 From: palmtree2013 Date: Tue, 31 Jan 2023 22:07:10 +0100 Subject: [PATCH 08/11] second step toward delaycoevolve --- src/DelaySSAToolkit.jl | 2 +- src/delayaggregator/aggregators.jl | 2 + src/delayproblem.jl | 71 +++++++++++++++++++----------- 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/DelaySSAToolkit.jl b/src/DelaySSAToolkit.jl index 05f080c8..bdc271dc 100644 --- a/src/DelaySSAToolkit.jl +++ b/src/DelaySSAToolkit.jl @@ -15,7 +15,7 @@ import JumpProcesses: AbstractJumpAggregator, AbstractJump, JumpProblem, Constan import JumpProcesses: make_dependency_graph, add_self_dependencies!, var_to_jumps_map # other functions import JumpProcesses: using_params, get_jump_info_fwrappers, isinplace_jump, extend_problem, - build_variable_callback, get_num_majumps + build_variable_callback, get_num_majumps, num_crjs, num_bndvrjs, supports_variablerates # using Catalyst using ModelingToolkit diff --git a/src/delayaggregator/aggregators.jl b/src/delayaggregator/aggregators.jl index f6d5aaf4..7fd30665 100644 --- a/src/delayaggregator/aggregators.jl +++ b/src/delayaggregator/aggregators.jl @@ -56,3 +56,5 @@ needs_depgraph(aggregator::DelayDirectCR) = true needs_vartojumps_map(aggregator::DelayDirectCR) = true needs_depgraph(aggregator::DelayCoevolve) = true needs_vartojumps_map(aggregator::DelayCoevolve) = true +supports_variablerates(aggregator::AbstractDelayAggregatorAlgorithm) = false +supports_variablerates(aggregator::DelayCoevolve) = true \ No newline at end of file diff --git a/src/delayproblem.jl b/src/delayproblem.jl index 39a8ed17..9daab949 100644 --- a/src/delayproblem.jl +++ b/src/delayproblem.jl @@ -99,7 +99,7 @@ end #BEGIN DelayJump mutable struct DelayJumpProblem{iip, P, A, C, J <: Union{Nothing, AbstractJumpAggregator}, - J2, J3, J4, J5, deType, K} <: + J2, J3, J4, J5, deType, R, K} <: DiffEqBase.AbstractJumpProblem{P, J} prob::P aggregator::A @@ -111,19 +111,20 @@ mutable struct DelayJumpProblem{iip, P, A, C, J <: Union{Nothing, AbstractJumpAg delayjumpsets::J5 de_chan0::deType save_delay_channel::Bool + rng::R kwargs::K end function DelayJumpProblem(p::P, a::A, dj::J, jc::C, vj::J2, rj::J3, mj::J4, djs::J5, - de_chan0::deType, save_delay_channel::Bool, - kwargs::K) where {P, A, J, C, J2, J3, J4, J5, deType, K} + de_chan0::deType, save_delay_channel::Bool, rng::R, + kwargs::K) where {P, A, J, C, J2, J3, J4, J5, deType, R, K} if !(typeof(a) <: AbstractDelayAggregatorAlgorithm) error("To solve DelayJumpProblem, one has to use one of the delay aggregators.") end iip = isinplace_jump(p, rj) - DelayJumpProblem{iip, P, A, C, J, J2, J3, J4, J5, deType, K}(p, a, dj, jc, vj, rj, mj, + DelayJumpProblem{iip, P, A, C, J, J2, J3, J4, J5, deType, R, K}(p, a, dj, jc, vj, rj, mj, djs, de_chan0, - save_delay_channel, kwargs) + save_delay_channel, rng, kwargs) end """ @@ -155,7 +156,7 @@ function DelayJumpProblem(prob, aggregator::AbstractDelayAggregatorAlgorithm, (false, true) : (true, true), rng = Xorshifts.Xoroshiro128Star(rand(UInt64)), scale_rates = false, useiszero = true, save_delay_channel = false, - callback = nothing, kwargs...) + callback = nothing, use_vrj_bounds = true, kwargs...) # initialize the MassActionJump rate constants with the user parameters if using_params(jumps.massaction_jump) @@ -169,41 +170,61 @@ function DelayJumpProblem(prob, aggregator::AbstractDelayAggregatorAlgorithm, maj = jumps.massaction_jump end + ndiscjumps = get_num_majumps(maj) + num_crjs(jumps) + + # separate bounded variable rate jumps *if* the aggregator can use them + if use_vrj_bounds && supports_variablerates(aggregator) && (num_bndvrjs(jumps) > 0) + bvrjs = filter(isbounded, jumps.variable_jumps) + cvrjs = filter(!isbounded, jumps.variable_jumps) + kwargs = merge((; variable_jumps = bvrjs), kwargs) + ndiscjumps += length(bvrjs) + else + bvrjs = nothing + cvrjs = jumps.variable_jumps + end + + ## Constant Rate Handling t, end_time, u = prob.tspan[1], prob.tspan[2], prob.u0 - if (typeof(jumps.constant_jumps) <: Tuple{}) && (maj === nothing) # check if there are no jumps - disc = nothing + + # handle majs, crjs, and bounded vrjs + if (ndiscjumps == 0) + disc_agg = nothing constant_jump_callback = CallbackSet() else - disc = aggregate(aggregator, u, prob.p, t, end_time, jumps.constant_jumps, maj, - save_positions, rng; kwargs...) - constant_jump_callback = DiscreteCallback(disc) + disc_agg = aggregate(aggregator, u, prob.p, t, end_time, jumps.constant_jumps, maj, + save_positions, rng; kwargs...) + constant_jump_callback = DiscreteCallback(disc_agg) end - iip = isinplace_jump(prob, jumps.regular_jump) - - ## Variable Rate Handling - if typeof(jumps.variable_jumps) <: Tuple{} + # handle any remaining vrjs + if length(cvrjs) > 0 + new_prob = extend_problem(prob, cvrjs; rng) + variable_jump_callback = build_variable_callback(CallbackSet(), 0, cvrjs...; rng) + cont_agg = cvrjs + else new_prob = prob variable_jump_callback = CallbackSet() - else - new_prob = extend_problem(prob, jumps) - variable_jump_callback = build_variable_callback(CallbackSet(), 0, - jumps.variable_jumps...) + cont_agg = JumpSet().variable_jumps end + + iip = isinplace_jump(prob, jumps.regular_jump) + + ## Variable Rate Handling + callbacks = CallbackSet(constant_jump_callback, variable_jump_callback) solkwargs = make_kwarg(; callback) DelayJumpProblem{iip, typeof(new_prob), typeof(aggregator), typeof(callbacks), - typeof(disc), typeof(jumps.variable_jumps), + typeof(disc_agg), typeof(cont_agg), typeof(jumps.regular_jump), typeof(maj), typeof(delayjumpsets), - typeof(de_chan0), typeof(solkwargs)}(new_prob, aggregator, disc, + typeof(de_chan0), typeof(rng), typeof(solkwargs)}(new_prob, aggregator, disc_agg, callbacks, jumps.variable_jumps, jumps.regular_jump, maj, delayjumpsets, de_chan0, - save_delay_channel, solkwargs) + save_delay_channel, rng, solkwargs) end make_kwarg(; kwargs...) = kwargs @@ -242,8 +263,8 @@ function DelayJumpProblem(js::JumpSystem, prob, aggregator, delayjumpset, de_cha majs = isempty(eqs.x[1]) ? nothing : assemble_maj(eqs.x[1], statetoid, majpmapper) crjs = ConstantRateJump[assemble_crj(js, j, statetoid) for j in eqs.x[2]] vrjs = VariableRateJump[assemble_vrj(js, j, statetoid) for j in eqs.x[3]] - ((prob isa DiscreteProblem) && !isempty(vrjs)) && - error("Use continuous problems such as an ODEProblem or a SDEProblem with VariableRateJumps") + # ((prob isa DiscreteProblem) && !isempty(vrjs)) && + # error("Use continuous problems such as an ODEProblem or a SDEProblem with VariableRateJumps") jset = JumpSet(Tuple(vrjs), Tuple(crjs), nothing, majs) if needs_vartojumps_map(aggregator) || needs_depgraph(aggregator) @@ -305,7 +326,7 @@ function DiffEqBase.remake(thing::DelayJumpProblem; kwargs...) DelayJumpProblem(dprob, thing.aggregator, thing.discrete_jump_aggregation, thing.jump_callback, thing.variable_jumps, thing.regular_jump, thing.massaction_jump, - delayjumpsets, de_chan0, thing.save_delay_channel, thing.kwargs) + delayjumpsets, de_chan0, thing.save_delay_channel, thing.rng, thing.kwargs) end function update_delayjumpsets(delayjumpsets::DelayJumpSet; kwargs...) From d115eddc9d9b136dc2e4d8cff8759ed0242d5687 Mon Sep 17 00:00:00 2001 From: xiaoming Date: Wed, 1 Feb 2023 10:11:04 +0100 Subject: [PATCH 09/11] experimental feature to support delaycoevolve --- Project.toml | 5 +- src/DelaySSAToolkit.jl | 6 +- src/delayproblem.jl | 2 +- test/Project.toml | 2 + test/hawkes_delay_test.jl | 156 ++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 1 + 6 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 test/hawkes_delay_test.jl diff --git a/Project.toml b/Project.toml index 69d8110c..5491e942 100644 --- a/Project.toml +++ b/Project.toml @@ -18,7 +18,7 @@ StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" [compat] -Catalyst = "12.0" + Catalyst = "12.0" DataStructures = "0.17, 0.18" DiffEqBase = "6.45" DocStringExtensions = "0.8, 0.9" @@ -34,6 +34,9 @@ julia = "1.6, 1.7, 1.8" [extras] Catalyst = "479239e8-5488-4da2-87a7-35f2df7eef83" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [targets] test = ["Test"] diff --git a/src/DelaySSAToolkit.jl b/src/DelaySSAToolkit.jl index bdc271dc..bb7afe80 100644 --- a/src/DelaySSAToolkit.jl +++ b/src/DelaySSAToolkit.jl @@ -14,8 +14,10 @@ import JumpProcesses: AbstractJumpAggregator, AbstractJump, JumpProblem, Constan # Dependency graph functions import JumpProcesses: make_dependency_graph, add_self_dependencies!, var_to_jumps_map # other functions -import JumpProcesses: using_params, get_jump_info_fwrappers, isinplace_jump, extend_problem, - build_variable_callback, get_num_majumps, num_crjs, num_bndvrjs, supports_variablerates +import JumpProcesses: using_params, get_jump_info_fwrappers, isinplace_jump, extend_problem, build_variable_callback, get_num_majumps, num_crjs + +# VariableRateJump +import JumpProcesses: isbounded, num_bndvrjs, supports_variablerates, haslrate, nullrate # using Catalyst using ModelingToolkit diff --git a/src/delayproblem.jl b/src/delayproblem.jl index 9daab949..fa35139f 100644 --- a/src/delayproblem.jl +++ b/src/delayproblem.jl @@ -221,7 +221,7 @@ function DelayJumpProblem(prob, aggregator::AbstractDelayAggregatorAlgorithm, typeof(jumps.regular_jump), typeof(maj), typeof(delayjumpsets), typeof(de_chan0), typeof(rng), typeof(solkwargs)}(new_prob, aggregator, disc_agg, callbacks, - jumps.variable_jumps, + cont_agg, jumps.regular_jump, maj, delayjumpsets, de_chan0, save_delay_channel, rng, solkwargs) diff --git a/test/Project.toml b/test/Project.toml index a9175b19..64e6f77f 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,6 +1,8 @@ [deps] Catalyst = "479239e8-5488-4da2-87a7-35f2df7eef83" JumpProcesses = "ccbc3e58-028d-4f4c-8cd5-9ae44345cda5" +OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" diff --git a/test/hawkes_delay_test.jl b/test/hawkes_delay_test.jl new file mode 100644 index 00000000..c4175881 --- /dev/null +++ b/test/hawkes_delay_test.jl @@ -0,0 +1,156 @@ +using DelaySSAToolkit, JumpProcesses, OrdinaryDiffEq, Statistics +using Test +using Random +rng = MersenneTwister(12345) +delay = 20.0 +function reset_history!(h; start_time = nothing) + @inbounds for i in 1:length(h) + h[i] = eltype(h)[] + end + nothing +end + +function empirical_rate(sol, agg) + if typeof(agg) <: DelayCoevolve + return (sol(sol.t[end]) - sol(sol.t[1] + delay)) / (sol.t[end] - sol.t[1] - delay) + else + return (sol(sol.t[end]) - sol(sol.t[1])) / (sol.t[end] - sol.t[1]) + end +end + +function hawkes_rate(i::Int, g, h) + function rate(u, p, t) + λ, α, β = p + x = zero(typeof(t)) + for j in g[i] + for _t in reverse(h[j]) + λij = α * exp(-β * (t - _t)) + if λij ≈ 0 + break + end + x += λij + end + end + return λ + x + end + return rate +end + +function hawkes_jump(i::Int, g, h, agg; uselrate = true) + rate = hawkes_rate(i, g, h) + urate = rate + if uselrate + lrate(u, p, t) = p[1] + rateinterval = (u, p, t) -> begin + _lrate = lrate(u, p, t) + _urate = urate(u, p, t) + return _urate == _lrate ? typemax(t) : 1 / (2 * _urate) + end + else + lrate = nothing + rateinterval = (u, p, t) -> begin + _urate = urate(u, p, t) + return 1 / (2 * _urate) + end + end + if typeof(agg) <: DelayCoevolve + affect! = (integrator) -> begin + push!(h[i], integrator.t) + integrator.u[i] += 0 + end + else + affect! = (integrator) -> begin + push!(h[i], integrator.t) + integrator.u[i] += 1 + end + end + return VariableRateJump(rate, affect!; lrate, urate, rateinterval) +end + +function hawkes_jump(u, g, h, agg; uselrate = true) + return [hawkes_jump(i, g, h, agg; uselrate) for i in 1:length(u)] +end + +function hawkes_problem(p, agg::DelayCoevolve; u = [0.0], tspan = (0.0, 50.0), + save_positions = (false, true), + g = [[1]], h = [[]], uselrate = true) + dprob = DiscreteProblem(u, tspan, p) + jumps = JumpSet(hawkes_jump(u, g, h, agg; uselrate)...) + de_chan0 = [[]] + delay_trigger = Dict(1=>[1=>delay]) # add a delay of 1.0 to the first jump + delay_complete = Dict(1=>[1=>1]) # complete the delay will duplicate 1 product + delay_interrupt = Dict() + delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) + jprob = DelayJumpProblem(dprob, agg, jumps, delayjumpset, de_chan0; dep_graph = g, save_positions, rng) + return jprob +end + +function f!(du, u, p, t) + du .= 0 + nothing +end + +function hawkes_problem(p, agg; u = [0.0], tspan = (0.0, 50.0), + save_positions = (false, true), + g = [[1]], h = [[]], kwargs...) + oprob = ODEProblem(f!, u, tspan, p) + jumps = hawkes_jump(u, g, h, agg) + jprob = JumpProblem(oprob, agg, jumps...; save_positions, rng) + return jprob +end + +function expected_stats_hawkes_problem(p, tspan, agg) + if typeof(agg) <: DelayCoevolve + T = tspan[end] - tspan[1] + delay + # stepper = SSAStepper() + else + T = tspan[end] - tspan[1] + end + λ, α, β = p + γ = β - α + κ = β / γ + Eλ = λ * κ + # Equation 21 + # J. Da Fonseca and R. Zaatour, + # “Hawkes Process: Fast Calibration, Application to Trade Clustering and Diffusive Limit.” + # Rochester, NY, Aug. 04, 2013. doi: 10.2139/ssrn.2294112. + Varλ = (Eλ * (T * κ^2 + (1 - κ^2) * (1 - exp(-T * γ)) / γ)) / (T^2) + return Eλ, Varλ +end + +u0 = [0.0] +p = (0.5, 0.5, 2.0) +tspan = (0.0, 250.0) +g = [[1]] +h = [Float64[]] + + + +aggs = (Direct(), DelayCoevolve(), DelayCoevolve()) +uselrate = zeros(Bool, length(aggs)) +uselrate[3] = true +Nsims = Int(5e2) + +for (i, agg) in enumerate(aggs) + @info "Testing $(typeof(agg))" + jump_prob = hawkes_problem(p, agg; u = u0, tspan, g, h, uselrate = uselrate[i]) + if typeof(agg) <: DelayCoevolve + stepper = SSAStepper() + else + stepper = Tsit5() + end + sols = Vector{ODESolution}(undef, Nsims) + for n in 1:Nsims + reset_history!(h) + sols[n] = solve(jump_prob, stepper) + end + if typeof(agg) <: DelayCoevolve + λs = permutedims(mapreduce((sol) -> empirical_rate(sol, agg), hcat, sols)) + else + cols = length(sols[1].u[1].u) + λs = permutedims(mapreduce((sol) -> empirical_rate(sol, agg), hcat, sols))[:, 1:cols] + end + Eλ, Varλ = expected_stats_hawkes_problem(p, tspan, agg) + @test isapprox(mean(λs), Eλ; atol = 0.01) + @test isapprox(var(λs), Varλ; atol = 0.001) +end diff --git a/test/runtests.jl b/test/runtests.jl index 21b212d0..e99e30dd 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,4 +9,5 @@ using Test, SafeTestsets @time @safetestset "delay problem test" begin include("delay_problem_test.jl") end @time @safetestset "remake problem test" begin include("remake_test.jl") end @time @safetestset "low level interface test" begin include("low_level_interface.jl") end + @time @safetestset "delay coevolve test" begin include("hawkes_delay_test.jl") end end From 9e87a1d7638578e941c67c50b26d70b02081f3a4 Mon Sep 17 00:00:00 2001 From: xiaoming Date: Wed, 1 Feb 2023 10:11:34 +0100 Subject: [PATCH 10/11] Bunp version to 0.2.4 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5491e942..006a34e8 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "DelaySSAToolkit" uuid = "d39380a9-d0d7-4ae6-ae63-0e8d8b7efa9e" authors = ["Xiaoming Fu"] -version = "0.2.3" +version = "0.2.4" [deps] DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" From 3320428c5ec3df015d3419aaaed05841816e72b9 Mon Sep 17 00:00:00 2001 From: xiaoming Date: Wed, 1 Feb 2023 10:24:11 +0100 Subject: [PATCH 11/11] format the doc --- src/DelaySSAToolkit.jl | 3 ++- src/delayaggregator/aggregators.jl | 3 ++- src/delayaggregator/delayssajump.jl | 5 +++-- src/delayproblem.jl | 30 +++++++++++++++++------------ test/hawkes_delay_test.jl | 18 ++++++++--------- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/DelaySSAToolkit.jl b/src/DelaySSAToolkit.jl index bb7afe80..f10b8263 100644 --- a/src/DelaySSAToolkit.jl +++ b/src/DelaySSAToolkit.jl @@ -14,7 +14,8 @@ import JumpProcesses: AbstractJumpAggregator, AbstractJump, JumpProblem, Constan # Dependency graph functions import JumpProcesses: make_dependency_graph, add_self_dependencies!, var_to_jumps_map # other functions -import JumpProcesses: using_params, get_jump_info_fwrappers, isinplace_jump, extend_problem, build_variable_callback, get_num_majumps, num_crjs +import JumpProcesses: using_params, get_jump_info_fwrappers, isinplace_jump, extend_problem, + build_variable_callback, get_num_majumps, num_crjs # VariableRateJump import JumpProcesses: isbounded, num_bndvrjs, supports_variablerates, haslrate, nullrate diff --git a/src/delayaggregator/aggregators.jl b/src/delayaggregator/aggregators.jl index 7fd30665..35ac8cf9 100644 --- a/src/delayaggregator/aggregators.jl +++ b/src/delayaggregator/aggregators.jl @@ -5,6 +5,7 @@ An abstract type that contains delay stochastic simulation algorithms: - DelayRejection - DelayMNRM - DelayDirectCR +- DelayCoevolve """ abstract type AbstractDelayAggregatorAlgorithm end """ @@ -57,4 +58,4 @@ needs_vartojumps_map(aggregator::DelayDirectCR) = true needs_depgraph(aggregator::DelayCoevolve) = true needs_vartojumps_map(aggregator::DelayCoevolve) = true supports_variablerates(aggregator::AbstractDelayAggregatorAlgorithm) = false -supports_variablerates(aggregator::DelayCoevolve) = true \ No newline at end of file +supports_variablerates(aggregator::DelayCoevolve) = true diff --git a/src/delayaggregator/delayssajump.jl b/src/delayaggregator/delayssajump.jl index 40b73820..acf9038c 100644 --- a/src/delayaggregator/delayssajump.jl +++ b/src/delayaggregator/delayssajump.jl @@ -352,7 +352,8 @@ input::Vector{Int} species indices output::Vector{Int} reactions need to be updated """ -function vars_to_jumps_delay(vartojumps_map::Vector{Vector{Int}}, vars::Vector{Int})::Vector{Int} +function vars_to_jumps_delay(vartojumps_map::Vector{Vector{Int}}, + vars::Vector{Int})::Vector{Int} jumps = Vector{Int}[] for var in vars push!(jumps, vartojumps_map[var]) @@ -398,7 +399,7 @@ julia> find_next_delay_vec(A, x) ([1, 2, 3], [2, 1, 1]) ``` """ -function find_next_delay_vec(de_chan::Vector{Vector{T}}, ttnj::T) where {T<:Real} +function find_next_delay_vec(de_chan::Vector{Vector{T}}, ttnj::T) where {T <: Real} position_indices = Vector{Int64}() num_in_vec = Vector{Int64}() for idx in eachindex(de_chan) diff --git a/src/delayproblem.jl b/src/delayproblem.jl index fa35139f..8d7197c1 100644 --- a/src/delayproblem.jl +++ b/src/delayproblem.jl @@ -122,9 +122,11 @@ function DelayJumpProblem(p::P, a::A, dj::J, jc::C, vj::J2, rj::J3, mj::J4, djs: error("To solve DelayJumpProblem, one has to use one of the delay aggregators.") end iip = isinplace_jump(p, rj) - DelayJumpProblem{iip, P, A, C, J, J2, J3, J4, J5, deType, R, K}(p, a, dj, jc, vj, rj, mj, - djs, de_chan0, - save_delay_channel, rng, kwargs) + DelayJumpProblem{iip, P, A, C, J, J2, J3, J4, J5, deType, R, K}(p, a, dj, jc, vj, rj, + mj, + djs, de_chan0, + save_delay_channel, rng, + kwargs) end """ @@ -182,7 +184,6 @@ function DelayJumpProblem(prob, aggregator::AbstractDelayAggregatorAlgorithm, bvrjs = nothing cvrjs = jumps.variable_jumps end - ## Constant Rate Handling t, end_time, u = prob.tspan[1], prob.tspan[2], prob.u0 @@ -219,12 +220,16 @@ function DelayJumpProblem(prob, aggregator::AbstractDelayAggregatorAlgorithm, DelayJumpProblem{iip, typeof(new_prob), typeof(aggregator), typeof(callbacks), typeof(disc_agg), typeof(cont_agg), typeof(jumps.regular_jump), typeof(maj), typeof(delayjumpsets), - typeof(de_chan0), typeof(rng), typeof(solkwargs)}(new_prob, aggregator, disc_agg, - callbacks, - cont_agg, - jumps.regular_jump, maj, - delayjumpsets, de_chan0, - save_delay_channel, rng, solkwargs) + typeof(de_chan0), typeof(rng), typeof(solkwargs)}(new_prob, aggregator, + disc_agg, + callbacks, + cont_agg, + jumps.regular_jump, + maj, + delayjumpsets, + de_chan0, + save_delay_channel, + rng, solkwargs) end make_kwarg(; kwargs...) = kwargs @@ -264,7 +269,7 @@ function DelayJumpProblem(js::JumpSystem, prob, aggregator, delayjumpset, de_cha crjs = ConstantRateJump[assemble_crj(js, j, statetoid) for j in eqs.x[2]] vrjs = VariableRateJump[assemble_vrj(js, j, statetoid) for j in eqs.x[3]] # ((prob isa DiscreteProblem) && !isempty(vrjs)) && - # error("Use continuous problems such as an ODEProblem or a SDEProblem with VariableRateJumps") + # error("Use continuous problems such as an ODEProblem or a SDEProblem with VariableRateJumps") jset = JumpSet(Tuple(vrjs), Tuple(crjs), nothing, majs) if needs_vartojumps_map(aggregator) || needs_depgraph(aggregator) @@ -326,7 +331,8 @@ function DiffEqBase.remake(thing::DelayJumpProblem; kwargs...) DelayJumpProblem(dprob, thing.aggregator, thing.discrete_jump_aggregation, thing.jump_callback, thing.variable_jumps, thing.regular_jump, thing.massaction_jump, - delayjumpsets, de_chan0, thing.save_delay_channel, thing.rng, thing.kwargs) + delayjumpsets, de_chan0, thing.save_delay_channel, thing.rng, + thing.kwargs) end function update_delayjumpsets(delayjumpsets::DelayJumpSet; kwargs...) diff --git a/test/hawkes_delay_test.jl b/test/hawkes_delay_test.jl index c4175881..1969d350 100644 --- a/test/hawkes_delay_test.jl +++ b/test/hawkes_delay_test.jl @@ -54,12 +54,12 @@ function hawkes_jump(i::Int, g, h, agg; uselrate = true) end end if typeof(agg) <: DelayCoevolve - affect! = (integrator) -> begin + affect! = (integrator) -> begin push!(h[i], integrator.t) integrator.u[i] += 0 end else - affect! = (integrator) -> begin + affect! = (integrator) -> begin push!(h[i], integrator.t) integrator.u[i] += 1 end @@ -77,11 +77,12 @@ function hawkes_problem(p, agg::DelayCoevolve; u = [0.0], tspan = (0.0, 50.0), dprob = DiscreteProblem(u, tspan, p) jumps = JumpSet(hawkes_jump(u, g, h, agg; uselrate)...) de_chan0 = [[]] - delay_trigger = Dict(1=>[1=>delay]) # add a delay of 1.0 to the first jump - delay_complete = Dict(1=>[1=>1]) # complete the delay will duplicate 1 product + delay_trigger = Dict(1 => [1 => delay]) # add a delay of 1.0 to the first jump + delay_complete = Dict(1 => [1 => 1]) # complete the delay will duplicate 1 product delay_interrupt = Dict() delayjumpset = DelayJumpSet(delay_trigger, delay_complete, delay_interrupt) - jprob = DelayJumpProblem(dprob, agg, jumps, delayjumpset, de_chan0; dep_graph = g, save_positions, rng) + jprob = DelayJumpProblem(dprob, agg, jumps, delayjumpset, de_chan0; dep_graph = g, + save_positions, rng) return jprob end @@ -109,7 +110,7 @@ function expected_stats_hawkes_problem(p, tspan, agg) λ, α, β = p γ = β - α κ = β / γ - Eλ = λ * κ + Eλ = λ * κ # Equation 21 # J. Da Fonseca and R. Zaatour, # “Hawkes Process: Fast Calibration, Application to Trade Clustering and Diffusive Limit.” @@ -124,8 +125,6 @@ tspan = (0.0, 250.0) g = [[1]] h = [Float64[]] - - aggs = (Direct(), DelayCoevolve(), DelayCoevolve()) uselrate = zeros(Bool, length(aggs)) uselrate[3] = true @@ -148,7 +147,8 @@ for (i, agg) in enumerate(aggs) λs = permutedims(mapreduce((sol) -> empirical_rate(sol, agg), hcat, sols)) else cols = length(sols[1].u[1].u) - λs = permutedims(mapreduce((sol) -> empirical_rate(sol, agg), hcat, sols))[:, 1:cols] + λs = permutedims(mapreduce((sol) -> empirical_rate(sol, agg), hcat, sols))[:, + 1:cols] end Eλ, Varλ = expected_stats_hawkes_problem(p, tspan, agg) @test isapprox(mean(λs), Eλ; atol = 0.01)