Skip to content

Commit 5cb6d35

Browse files
committed
fix rates and minimize generated expression ops
1 parent 9a17f73 commit 5cb6d35

File tree

1 file changed

+69
-45
lines changed

1 file changed

+69
-45
lines changed
Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,57 @@
1-
struct Reaction{T <: Number}
1+
struct Reaction{S <: Variable, T <: Number}
22
rate
33
substrates::Vector{Operation}
44
products::Vector{Operation}
55
substoich::Vector{T}
6-
prodstoich::Vector{T}
6+
prodstoich::Vector{T}
7+
netstoich::Vector{Pair{S,T}}
8+
only_use_rate::Bool
79
end
810

11+
function Reaction(rate, subs, prods, substoich, prodstoich;
12+
netstoich=nothing, only_use_rate=false, kwargs...)
13+
14+
ns = isnothing(netstoich) ? get_netstoich(subs, prods, substoich, prodstoich) : netstoich
15+
Reaction(rate, subs, prods, substoich, prodstoich, ns, only_use_rate)
16+
end
17+
18+
# three argument constructor assumes stoichiometric coefs are one and integers
19+
Reaction(rate, subs, prods; kwargs...) = Reaction(rate, subs, prods,
20+
ones(Int,length(subs)),
21+
ones(Int,length(prods));
22+
kwargs...)
23+
24+
# calculates the net stoichiometry of a reaction as a vector of pairs (sub,substoich)
25+
function get_netstoich(subs, prods, sstoich, pstoich)
26+
# stoichiometry as a Dictionary
27+
nsdict = Dict(sub.op => -sstoich[i] for (i,sub) in enumerate(subs))
28+
for (i,p) in enumerate(prods)
29+
coef = pstoich[i]
30+
prod = p.op
31+
@inbounds nsdict[prod] = haskey(nsdict, prod) ? nsdict[prod] + coef : coef
32+
end
33+
34+
# stoichiometry as a vector
35+
ns = [el for el in nsdict if el[2] != zero(el[2])]
36+
37+
ns
38+
end
39+
40+
941
struct ReactionSystem <: AbstractSystem
1042
eqs::Vector{Reaction}
1143
iv::Variable
12-
species::Vector{Variable}
13-
params::Vector{Variable}
44+
states::Vector{Variable}
45+
ps::Vector{Variable}
1446
name::Symbol
1547
systems::Vector{ReactionSystem}
1648
end
1749

18-
function ReactionSystem(eqs, iv, species, params;
19-
systems = ReactionSystem[],
20-
name = gensym(:ReactionSystem))
50+
function ReactionSystem(eqs, iv, species, params; systems = ReactionSystem[],
51+
name = gensym(:ReactionSystem))
2152

22-
ReactionSystem(eqs, iv, convert.(Variable,species),
23-
convert.(Variable,params), name, systems)
53+
ReactionSystem(eqs, iv, convert.(Variable,species), convert.(Variable,params),
54+
name, systems)
2455
end
2556

2657
# Calculate the ODE rate law
@@ -37,61 +68,54 @@ function oderatelaw(rx)
3768
rl
3869
end
3970

40-
function essemble_drift(rs)
71+
function assemble_drift(rs)
4172
D = Differential(rs.iv())
42-
eqs = [D(x(rs.iv())) ~ 0 for x in rs.species]
43-
species_to_idx = Dict(enumerate(rs.species))
73+
eqs = [D(x(rs.iv())) ~ 0 for x in rs.states]
74+
species_to_idx = Dict((x => i for (i,x) in enumerate(rs.states)))
4475

45-
# note, this should really use the net stoichiometry to avoid
46-
# adding and substracting the same term for A + X -> B + X
4776
for rx in rs.eqs
48-
rl = oderatelaw(rx.rate, rx.substrates, rx.substoich)
49-
stoich = rx.substoich
50-
for (sidx,substrate) in enumerate(rx.substrates)
51-
i = species_to_idx[substrate.op]
52-
eqs[i] = Equation(eqs[i].lhs, eqs[i].rhs - stoich[sidx]*rl)
53-
end
54-
55-
stoich = rx.prodstoich
56-
for (pidx,product) in enumerate(rx.products)
57-
i = species_to_idx[product.op]
58-
eqs[i] = Equation(eqs[i].lhs,eqs[i].rhs + stoich[pidx]*rl)
77+
rl = oderatelaw(rx)
78+
for (spec,stoich) in rx.netstoich
79+
i = species_to_idx[spec]
80+
if iszero(eqs[i].rhs)
81+
Δspec = (stoich == one(stoich)) ? rl : stoich * rl
82+
eqs[i] = Equation(eqs[i].lhs, Δspec)
83+
else
84+
Δspec = (abs(stoich) == one(stoich)) ? rl : abs(stoich) * rl
85+
if stoich > zero(stoich)
86+
eqs[i] = Equation(eqs[i].lhs, eqs[i].rhs + Δspec)
87+
else
88+
eqs[i] = Equation(eqs[i].lhs, eqs[i].rhs - Δspec)
89+
end
90+
end
5991
end
6092
end
61-
6293
eqs
6394
end
6495

65-
function essemble_diffusion(rs)
66-
eqs = Expression[Constant(0) for x in rs.species, y in rs.eqs]
67-
species_to_idx = Dict(enumerate(rs.species))
96+
function assemble_diffusion(rs)
97+
eqs = Expression[Constant(0) for x in rs.states, y in rs.eqs]
98+
species_to_idx = Dict((x => i for (i,x) in enumerate(rs.states)))
6899

69100
for (j,rx) in enumerate(rs.eqs)
70-
rlsqrt = sqrt(oderatelaw(rx.rate, rx.substrates, rx.substoich))
71-
stoich = rx.substoich
72-
for (sidx,substrate) in enumerate(rx.substrates)
73-
i = species_to_idx[substrate.op]
74-
eqs[i,j] = -stoich[sidx] * rlsqrt
75-
end
76-
77-
for (pidx,product) in enumerate(rx.products)
78-
i = species_to_idx[product.op]
79-
Δp = stoich[pidx] * rlsqrt
80-
eqs[i,j] = (eqs[i,j]==Constant(0)) ? Δp : (eqs[i,j] + Δp)
101+
rlsqrt = sqrt(oderatelaw(rx))
102+
for (spec,stoich) in rx.netstoich
103+
i = species_to_idx[spec]
104+
eqs[i,j] = (stoich == one(stoich)) ? rlsqrt : stoich * rlsqrt
81105
end
82106
end
83107
eqs
84108
end
85109

86110
function Base.convert(::Type{<:ODESystem},rs::ReactionSystem)
87-
eqs = essemble_drift(rs)
88-
ODESystem(eqs,rs.iv,rs.species,rs.params,name=rs.name,
111+
eqs = assemble_drift(rs)
112+
ODESystem(eqs,rs.iv,rs.states,rs.ps,name=rs.name,
89113
systems=convert.(ODESystem,rs.systems))
90114
end
91115

92116
function Base.convert(::Type{<:SDESystem},rs::ReactionSystem)
93-
eqs = essemble_drift(rs)
94-
noiseeqs = essemble_diffusion(rs)
95-
SDESystem(eqs,noiseeqs,rs.iv,rs.species,rs.params,
117+
eqs = assemble_drift(rs)
118+
noiseeqs = assemble_diffusion(rs)
119+
SDESystem(eqs,noiseeqs,rs.iv,rs.states,rs.ps,
96120
name=rs.name,systems=convert.(SDESystem,rs.systems))
97121
end

0 commit comments

Comments
 (0)