@@ -82,6 +82,8 @@ function DiffEqBase.solve(jump_prob::JumpProblem, alg::SimpleTauLeaping;
82
82
end
83
83
84
84
function compute_hor (reactant_stoch, numjumps)
85
+ # Compute the highest order of reaction (HOR) for each reaction j, as per Cao et al. (2006), Section IV.
86
+ # HOR is the sum of stoichiometric coefficients of reactants in reaction j.
85
87
hor = zeros (Int, numjumps)
86
88
for j in 1 : numjumps
87
89
order = sum (stoch for (spec_idx, stoch) in reactant_stoch[j]; init= 0 )
@@ -94,7 +96,9 @@ function compute_hor(reactant_stoch, numjumps)
94
96
end
95
97
96
98
function precompute_reaction_conditions (reactant_stoch, hor, numspecies, numjumps)
97
- # Initialize reaction_conditions as a vector of vectors of tuples
99
+ # Precompute reaction conditions for each species i, storing reactions j where i is a reactant,
100
+ # along with stoichiometry (nu_ij) and HOR (hor_j), to optimize compute_gi.
101
+ # Reactant stoichiometry is used per Cao et al. (2006), Section IV, for g_i calculations.
98
102
reaction_conditions = [Vector () for _ in 1 : numspecies]
99
103
for j in 1 : numjumps
100
104
for (spec_idx, stoch) in reactant_stoch[j]
@@ -107,6 +111,14 @@ function precompute_reaction_conditions(reactant_stoch, hor, numspecies, numjump
107
111
end
108
112
109
113
function compute_gi (u, reaction_conditions, i)
114
+ # Compute g_i for species i, bounding the relative change in propensity functions,
115
+ # as per Cao et al. (2006), Section IV (between equations 27-28).
116
+ # g_i is the maximum over all reactions j where species i is a reactant:
117
+ # - HOR = 1: g_i = 1
118
+ # - HOR = 2, nu_ij = 1: g_i = 2
119
+ # - HOR = 2, nu_ij >= 2: g_i = x_i * (2/nu_ij + 1/(nu_ij - 1))
120
+ # - HOR = 3, nu_ij = 1: g_i = 3
121
+ # - HOR = 3, nu_ij >= 2: g_i = 1.5 * x_i * (2/nu_ij + 1/(nu_ij - 1))
110
122
max_gi = 1
111
123
for (j, nu_ij, hor_j) in reaction_conditions[i]
112
124
if hor_j == 1
@@ -131,20 +143,25 @@ function compute_gi(u, reaction_conditions, i)
131
143
end
132
144
133
145
function compute_tau_explicit (u, rate_cache, nu, p, t, epsilon, rate, dtmin, reaction_conditions, numjumps)
146
+ # Compute the tau-leaping step-size using equation (8) from Cao et al. (2006):
147
+ # tau = min_{i in I_rs} { max(epsilon * x_i / g_i, 1) / |mu_i(x)|, max(epsilon * x_i / g_i, 1)^2 / sigma_i^2(x) }
148
+ # where mu_i(x) and sigma_i^2(x) are defined in equations (9a) and (9b):
149
+ # mu_i(x) = sum_j nu_ij * a_j(x), sigma_i^2(x) = sum_j nu_ij^2 * a_j(x)
150
+ # I_rs is the set of reactant species (assumed to be all species here, as critical reactions are not specified).
134
151
rate (rate_cache, u, p, t)
135
152
tau = Inf
136
153
for i in 1 : length (u)
137
154
mu = zero (eltype (u))
138
155
sigma2 = zero (eltype (u))
139
156
for j in 1 : size (nu, 2 )
140
- mu += nu[i, j] * rate_cache[j]
141
- sigma2 += nu[i, j]^ 2 * rate_cache[j]
157
+ mu += nu[i, j] * rate_cache[j] # Equation (9a)
158
+ sigma2 += nu[i, j]^ 2 * rate_cache[j] # Equation (9b)
142
159
end
143
160
gi = compute_gi (u, reaction_conditions, i)
144
- bound = max (epsilon * u[i] / gi, 1.0 )
145
- mu_term = abs (mu) > 0 ? bound / abs (mu) : Inf
146
- sigma_term = sigma2 > 0 ? bound^ 2 / sigma2 : Inf
147
- tau = min (tau, mu_term, sigma_term)
161
+ bound = max (epsilon * u[i] / gi, 1.0 ) # max(epsilon * x_i / g_i, 1)
162
+ mu_term = abs (mu) > 0 ? bound / abs (mu) : Inf # First term in equation (8)
163
+ sigma_term = sigma2 > 0 ? bound^ 2 / sigma2 : Inf # Second term in equation (8)
164
+ tau = min (tau, mu_term, sigma_term) # Equation (8)
148
165
end
149
166
return max (tau, dtmin)
150
167
end
@@ -229,7 +246,7 @@ function DiffEqBase.solve(jump_prob::JumpProblem, alg::SimpleAdaptiveTauLeaping;
229
246
end
230
247
u_new = u_current + du
231
248
if any (< (0 ), u_new)
232
- # Halve tau to avoid negative populations, as per Cao et al. (2006, J. Chem. Phys., DOI: 10.1063/1.2159468)
249
+ # Halve tau to avoid negative populations, as per Cao et al. (2006), Section 3.3
233
250
tau /= 2
234
251
continue
235
252
end
0 commit comments