Skip to content

Commit ea99c8b

Browse files
authored
Revert "Update Reaction constructor"
1 parent ae6cf0a commit ea99c8b

File tree

3 files changed

+69
-141
lines changed

3 files changed

+69
-141
lines changed

src/reaction.jl

Lines changed: 64 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,6 @@ function metadata_only_use_rate_check(metadata)
7272
return Bool(metadata[only_use_rate_idx][2])
7373
end
7474

75-
# Used to promote a vector to the appropriate type. Takes the `vec == Any[]` case into account by
76-
# returning an empty vector of the appropriate type.
77-
function promote_reaction_vector(vec, type)
78-
isempty(vec) && (return type[])
79-
type[value(v) for v in vec]
80-
end
81-
8275
# calculates the net stoichiometry of a reaction as a vector of pairs (sub,substoich)
8376
function get_netstoich(subs, prods, sstoich, pstoich)
8477
# stoichiometry as a Dictionary
@@ -92,6 +85,9 @@ function get_netstoich(subs, prods, sstoich, pstoich)
9285
[el for el in nsdict if !_iszero(el[2])]
9386
end
9487

88+
# Get the net stoichiometries' type.
89+
netstoich_stoichtype(::Vector{Pair{S, T}}) where {S, T} = T
90+
9591
### Reaction Structure ###
9692

9793
"""
@@ -138,19 +134,19 @@ Notes:
138134
- The three-argument form assumes all reactant and product stoichiometric coefficients
139135
are one.
140136
"""
141-
struct Reaction{T}
137+
struct Reaction{S, T}
142138
"""The rate function (excluding mass action terms)."""
143139
rate::Any
144140
"""Reaction substrates."""
145-
substrates::Vector{Any}
141+
substrates::Vector
146142
"""Reaction products."""
147-
products::Vector{Any}
143+
products::Vector
148144
"""The stoichiometric coefficients of the reactants."""
149-
substoich::Vector
145+
substoich::Vector{T}
150146
"""The stoichiometric coefficients of the products."""
151-
prodstoich::Vector
147+
prodstoich::Vector{T}
152148
"""The net stoichiometric coefficients of all species changed by the reaction."""
153-
netstoich::Vector{Pair{Any, T}}
149+
netstoich::Vector{Pair{S, T}}
154150
"""
155151
`false` (default) if `rate` should be multiplied by mass action terms to give the rate law.
156152
`true` if `rate` represents the full reaction rate law.
@@ -164,74 +160,83 @@ struct Reaction{T}
164160
end
165161

166162
# Five-argument constructor accepting rate, substrates, and products, and their stoichiometries.
167-
function Reaction(rate, subs::Vector, prods::Vector, substoich::Vector{S}, prodstoich::Vector{T};
168-
netstoich = [], metadata = Pair{Symbol, Any}[],
169-
only_use_rate = metadata_only_use_rate_check(metadata), kwargs...) where {S,T}
170-
# Error checks.
171-
isempty(subs) && isempty(prods) &&
172-
throw(ArgumentError("A reaction requires either a non-empty substrate or product vector."))
173-
length(subs) != length(substoich) &&
174-
throw(ArgumentError("The substrate vector ($(subs)) and the substrate stoichiometry vector ($(substoich)) must have equal length."))
175-
length(prods) != length(prodstoich) &&
176-
throw(ArgumentError("The product vector ($(prods)) and the product stoichiometry vector ($(prodstoich)) must have equal length."))
163+
function Reaction(rate, subs, prods, substoich, prodstoich;
164+
netstoich = nothing, metadata = Pair{Symbol, Any}[],
165+
only_use_rate = metadata_only_use_rate_check(metadata), kwargs...)
166+
(isnothing(prods) && isnothing(subs)) &&
167+
throw(ArgumentError("A reaction requires a non-nothing substrate or product vector."))
168+
(isnothing(prodstoich) && isnothing(substoich)) &&
169+
throw(ArgumentError("Both substrate and product stochiometry inputs cannot be nothing."))
170+
171+
if isnothing(subs)
172+
prodtype = typeof(value(first(prods)))
173+
subs = Vector{prodtype}()
174+
!isnothing(substoich) &&
175+
throw(ArgumentError("If substrates are nothing, substrate stoichiometries have to be so too."))
176+
substoich = typeof(prodstoich)()
177+
else
178+
subs = value.(subs)
179+
end
177180
allunique(subs) ||
178181
throw(ArgumentError("Substrates can not be repeated in the list provided to `Reaction`, please modify the stoichiometry for any repeated substrates instead."))
182+
S = eltype(substoich)
183+
184+
if isnothing(prods)
185+
prods = Vector{eltype(subs)}()
186+
!isnothing(prodstoich) &&
187+
throw(ArgumentError("If products are nothing, product stoichiometries have to be so too."))
188+
prodstoich = typeof(substoich)()
189+
else
190+
prods = value.(prods)
191+
end
179192
allunique(prods) ||
180193
throw(ArgumentError("Products can not be repeated in the list provided to `Reaction`, please modify the stoichiometry for any repeated products instead."))
194+
T = eltype(prodstoich)
181195

182-
# Ensures everything have uniform and correct types.
183-
subs = promote_reaction_vector(subs, BasicSymbolic{Real})
184-
prods = promote_reaction_vector(prods, BasicSymbolic{Real})
196+
# try to get a common type for stoichiometry, using Any if have Syms
185197
stoich_type = promote_type(S, T)
186-
(stoich_type <: Num) && (stoich_type = Any)
187-
substoich = promote_reaction_vector(substoich, stoich_type)
188-
prodstoich = promote_reaction_vector(prodstoich, stoich_type)
198+
if stoich_type <: Num
199+
stoich_type = Any
200+
substoich′ = Any[value(s) for s in substoich]
201+
prodstoich′ = Any[value(p) for p in prodstoich]
202+
else
203+
substoich′ = (S == stoich_type) ? substoich : convert.(stoich_type, substoich)
204+
prodstoich′ = (T == stoich_type) ? prodstoich : convert.(stoich_type, prodstoich)
205+
end
189206

190-
# Checks that all reactants are valid.
191207
if !(all(isvalidreactant, subs) && all(isvalidreactant, prods))
192208
badsts = union(filter(!isvalidreactant, subs), filter(!isvalidreactant, prods))
193-
throw(ArgumentError("To be a valid substrate or product, non-constant species must be declared via @species, while constant species must be parameters with the isconstantspecies metadata. The following reactants do not follow this convention:\n $badsts"))
209+
throw(ArgumentError("""To be a valid substrate or product, non-constant species must be declared via @species, while constant species must be parameters with the isconstantspecies metadata. The following reactants do not follow this convention:\n $badsts"""))
194210
end
195-
196-
# Computes the net stoichiometries.
197-
if isempty(netstoich)
198-
netstoich = get_netstoich(subs, prods, substoich, prodstoich)
199-
elseif typeof(netstoich) != Vector{Pair{BasicSymbolic{Real}, stoich_type}}
200-
netstoich = Pair{BasicSymbolic{Real}, stoich_type}[
201-
value(ns[1]) => convert(stoich_type, ns[2]) for ns in netstoich]
211+
212+
ns = if netstoich === nothing
213+
get_netstoich(subs, prods, substoich′, prodstoich′)
214+
else
215+
(netstoich_stoichtype(netstoich) != stoich_type) ?
216+
convert.(stoich_type, netstoich) : netstoich
202217
end
203218

204-
# Handles metadata (check that all entries are unique, remove potential `only_use_rate`
205-
# entries, and converts to the required type.
219+
# Check that all metadata entries are unique. (cannot use `in` since some entries may be symbolics).
206220
if !allunique(entry[1] for entry in metadata)
207221
error("Repeated entries for the same metadata encountered in the following metadata set: $([entry[1] for entry in metadata]).")
208222
end
223+
224+
# Deletes potential `:only_use_rate => ` entries from the metadata.
209225
if any(:only_use_rate == entry[1] for entry in metadata)
210226
deleteat!(metadata, findfirst(:only_use_rate == entry[1] for entry in metadata))
211227
end
212-
metadata = convert(Vector{Pair{Symbol, Any}}, metadata)
213228

214-
Reaction{stoich_type}(value(rate), subs, prods, substoich, prodstoich, netstoich, only_use_rate, metadata)
215-
end
229+
# Ensures metadata have the correct type.
230+
metadata = convert(Vector{Pair{Symbol, Any}}, metadata)
216231

217-
# Three-argument constructor. Handles the case where no stoichiometries is given
218-
# (by assuming that all stoichiometries are `1`).
219-
function Reaction(rate, subs::Vector, prods::Vector; kwargs...)
220-
Reaction(rate, subs, prods, ones(Int, length(subs)), ones(Int, length(prods)); kwargs...)
232+
Reaction(value(rate), subs, prods, substoich′, prodstoich′, ns, only_use_rate, metadata)
221233
end
222234

223-
# Handles cases where `nothing` is given (instead of an empty vector).
224-
function Reaction(rate, subs::Vector, prods::Nothing, substoich::Vector, prodstoich::Nothing; kwargs...)
225-
Reaction(rate, subs, Int64[], substoich, Int64[]; kwargs...)
226-
end
227-
function Reaction(rate, subs::Nothing, prods::Vector, substoich::Nothing, prodstoich::Vector; kwargs...)
228-
Reaction(rate, Int64[], prods, Int64[], prodstoich; kwargs...)
229-
end
230-
function Reaction(rate, subs::Vector, prods::Nothing; kwargs...)
231-
Reaction(rate, subs, Int64[]; kwargs...)
232-
end
233-
function Reaction(rate, subs::Nothing, prods::Vector; kwargs...)
234-
Reaction(rate, Int64[], prods; kwargs...)
235+
# Three argument constructor assumes stoichiometric coefs are one and integers.
236+
function Reaction(rate, subs, prods; kwargs...)
237+
sstoich = isnothing(subs) ? nothing : ones(Int, length(subs))
238+
pstoich = isnothing(prods) ? nothing : ones(Int, length(prods))
239+
Reaction(rate, subs, prods, sstoich, pstoich; kwargs...)
235240
end
236241

237242
# Union type for `Reaction`s and `Equation`s.

test/reactionsystem_core/reaction.jl

Lines changed: 4 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -36,94 +36,13 @@ let
3636
@test issetequal(get_symbolics(rx4), [X, t, Y, η1, η2])
3737
end
3838

39-
### Reaction Constructor Tests ###
40-
41-
# Sets the default `t` to use.
42-
t = default_t()
43-
44-
# Checks that `Reaction`s can be sucesfully created using various complicated inputs.
45-
# Checks that the `Reaction`s have the correct type, and the correct net stoichiometries are generated.
46-
let
47-
# Declare symbolic variables.
48-
@parameters k n1 n2::Int32 x [isconstantspecies=true]
49-
@species X(t) Y(t) Z(t)
50-
@variables A(t)
51-
52-
# Creates `Reaction`s.
53-
rx1 = Reaction(k*A, [X], [])
54-
rx2 = Reaction(k*A, [x], [Y], [1.5], [1])
55-
rx3 = Reaction(k*A, [x, X], [], [n1 + n2, n2], [])
56-
rx4 = Reaction(k*A, [X, Y], [X, Y, Z], [2//3, 3], [1//3, 1, 2])
57-
rx5 = Reaction(k*A, [X, Y], [X, Y, Z], [2, 3], [1, n1, n2])
58-
rx6 = Reaction(k*A, [X], [x], [n1], [1])
59-
60-
# Check `Reaction` types.
61-
@test rx1 isa Reaction{Int64}
62-
@test rx2 isa Reaction{Float64}
63-
@test rx3 isa Reaction{Any}
64-
@test rx4 isa Reaction{Rational{Int64}}
65-
@test rx5 isa Reaction{Any}
66-
@test rx6 isa Reaction{Any}
67-
68-
# Check `Reaction` net stoichiometries.
69-
issetequal(rx1.netstoich, [X => -1])
70-
issetequal(rx2.netstoich, [x => -1.5, Y => 1.0])
71-
issetequal(rx3.netstoich, [x => -n1 - n2, X => -n2])
72-
issetequal(rx4.netstoich, [X => -1//3, Y => -2//1, Z => 2//1])
73-
issetequal(rx5.netstoich, [X => -1, Y => n1 - 3, Z => n2])
74-
issetequal(rx6.netstoich, [X => -n1, x => 1])
75-
end
76-
77-
# Tests that various `Reaction` constructors gives identical inputs.
78-
let
79-
# Declare symbolic variables.
80-
@parameters k n1 n2::Int32
81-
@species X(t) Y(t) Z(t)
82-
@variables A(t)
83-
84-
# Tests that the three-argument constructor generates correct result.
85-
@test Reaction(k*A, [X], [Y, Z]) == Reaction(k*A, [X], [Y, Z], [1], [1, 1])
86-
87-
# Tests that `[]` and `nothing` can be used interchangeably.
88-
@test Reaction(k*A, [X, Z], nothing) == Reaction(k*A, [X, Z], [])
89-
@test Reaction(k*A, nothing, [Y, Z]) == Reaction(k*A, [], [Y, Z])
90-
@test Reaction(k*A, [X, Z], nothing, [n1 + n2, 2], nothing) == Reaction(k*A, [X, Z], [], [n1 + n2, 2], [])
91-
@test Reaction(k*A, nothing, [Y, Z], nothing, [n1 + n2, 2]) == Reaction(k*A, [], [Y, Z], [], [n1 + n2, 2])
92-
end
93-
94-
# Tests that various incorrect inputs yields errors.
95-
let
96-
# Declare symbolic variables.
97-
@parameters k n1 n2::Int32
98-
@species X(t) Y(t) Z(t)
99-
@variables A(t)
100-
101-
# Neither substrates nor products.
102-
@test_throws ArgumentError Reaction(k*A, [], [])
103-
104-
# Substrate vector not of equal length to substrate stoichiometry vector.
105-
@test_throws ArgumentError Reaction(k*A, [X, X, Z], [], [1, 2], [])
106-
107-
# Product vector not of equal length to product stoichiometry vector.
108-
@test_throws ArgumentError Reaction(k*A, [], [X, X, Z], [], [1, 2])
109-
110-
# Repeated substrates.
111-
@test_throws ArgumentError Reaction(k*A, [X, X, Z], [])
112-
113-
# Repeated products.
114-
@test_throws ArgumentError Reaction(k*A, [], [Y, Z, Z])
115-
116-
# Non-valid reactants (parameter or variable).
117-
@test_throws ArgumentError Reaction(k*A, [], [A])
118-
@test_throws ArgumentError Reaction(k*A, [], [k])
119-
end
120-
12139
### Tests Metadata ###
12240

12341
# Tests creation.
12442
# Tests basic accessor functions.
12543
# Tests that repeated metadata entries are not permitted.
12644
let
45+
@variables t
12746
@parameters k
12847
@species X(t) X2(t)
12948

@@ -141,6 +60,7 @@ end
14160

14261
# Tests accessors for system without metadata.
14362
let
63+
@variables t
14464
@parameters k
14565
@species X(t) X2(t)
14666

@@ -157,6 +77,7 @@ end
15777
# Tests basic accessor functions.
15878
# Tests various metadata types.
15979
let
80+
@variables t
16081
@parameters k
16182
@species X(t) X2(t)
16283

@@ -188,6 +109,7 @@ end
188109

189110
# Tests the noise scaling metadata.
190111
let
112+
@variables t
191113
@parameters k η
192114
@species X(t) X2(t)
193115

test/reactionsystem_core/reactionsystem.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ end
140140
### Check ODE, SDE, and Jump Functions ###
141141

142142
# Test by evaluating drift and diffusion terms.
143+
143144
let
144145
u = rnd_u0(rs, rng)
145146
p = rnd_ps(rs, rng)

0 commit comments

Comments
 (0)