Skip to content

Commit 40cd3b3

Browse files
authored
Merge pull request #1224 from isaacsas/expand_hills
Expand Catalyst functions when converting
2 parents 4ec857f + acee192 commit 40cd3b3

File tree

3 files changed

+197
-44
lines changed

3 files changed

+197
-44
lines changed

HISTORY.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,27 @@
123123
plot_network(brusselator)
124124
```
125125
- The letter Ø (used in Danish/Norwegian alphabet) is now conisdred the same as ∅ (empty set). It can no longer be used as a species/parameter.
126-
126+
- When converting a Catalyst `ReactionSystem` to a ModelingToolkit system, for
127+
example an `ODESystem`, Catalyst defined functions like `hill(A,B,C,D)` are
128+
now replaced with the explicit rational function they represent in the
129+
equations of the generated system. For example `mm(X,v,K)` will be replaced
130+
with `v*X / (X + K)`. This can be disabled by passing the keyword argument
131+
`expand_catalyst_funs = false`. e.g.
132+
```julia
133+
using Catalyst
134+
rn = @reaction_network begin
135+
hill(X,v,K,n), A --> 0
136+
end
137+
osys = convert(ODESystem, rn)
138+
```
139+
generates an ODE system with `D(A) ~ ~ -((v*A(t)*(X^n)) / (K^n + X^n))`, while
140+
```julia
141+
osys = convert(ODESystem, rn; expand_catalyst_funs = false)
142+
```
143+
generates an ODE system with `D(A) ~ -A(t)*hill(X, v, K, n)`. This keyword
144+
argument can also be passed to problems defined over `ReactionSystem`s, i.e.
145+
when calling `ODEProblem(rn, u0, tspan, p; expand_catalyst_funs = false)`.
146+
127147
## Catalyst 14.4.1
128148
- Support for user-defined functions on the RHS when providing coupled equations
129149
for CRNs using the @equations macro. For example, the following now works:

src/reactionsystem_conversions.jl

Lines changed: 73 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ Notes:
2222
`combinatoric_ratelaw=false` then the ratelaw is `k*S^2`, i.e. the scaling
2323
factor is ignored.
2424
"""
25-
function oderatelaw(rx; combinatoric_ratelaw = true)
25+
function oderatelaw(rx; combinatoric_ratelaw = true, expand_catalyst_funs = true)
2626
@unpack rate, substrates, substoich, only_use_rate = rx
2727
rl = rate
28+
expand_catalyst_funs && (rl = expand_registered_functions(rl))
2829

2930
# if the stoichiometric coefficients are not integers error if asking to scale rates
3031
!all(s -> s isa Union{Integer, Symbolic}, substoich) &&
@@ -46,7 +47,8 @@ end
4647
# including non-species variables.
4748
drop_dynamics(s) = isconstant(s) || isbc(s) || (!isspecies(s))
4849

49-
function assemble_oderhs(rs, ispcs; combinatoric_ratelaws = true, remove_conserved = false)
50+
function assemble_oderhs(rs, ispcs; combinatoric_ratelaws = true,
51+
remove_conserved = false, expand_catalyst_funs = true)
5052
nps = get_networkproperties(rs)
5153
species_to_idx = Dict(x => i for (i, x) in enumerate(ispcs))
5254
rhsvec = Any[0 for _ in ispcs]
@@ -57,7 +59,8 @@ function assemble_oderhs(rs, ispcs; combinatoric_ratelaws = true, remove_conserv
5759
end
5860

5961
for rx in get_rxs(rs)
60-
rl = oderatelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws)
62+
rl = oderatelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws,
63+
expand_catalyst_funs)
6164
remove_conserved && (rl = substitute(rl, depspec_submap))
6265
for (spec, stoich) in rx.netstoich
6366
# dependent species don't get an ODE, so are skipped
@@ -90,8 +93,9 @@ function assemble_oderhs(rs, ispcs; combinatoric_ratelaws = true, remove_conserv
9093
end
9194

9295
function assemble_drift(rs, ispcs; combinatoric_ratelaws = true, as_odes = true,
93-
include_zero_odes = true, remove_conserved = false)
94-
rhsvec = assemble_oderhs(rs, ispcs; combinatoric_ratelaws, remove_conserved)
96+
include_zero_odes = true, remove_conserved = false, expand_catalyst_funs = true)
97+
rhsvec = assemble_oderhs(rs, ispcs; combinatoric_ratelaws, remove_conserved,
98+
expand_catalyst_funs)
9599
if as_odes
96100
D = Differential(get_iv(rs))
97101
eqs = [Equation(D(x), rhs)
@@ -104,7 +108,7 @@ end
104108

105109
# this doesn't work with constraint equations currently
106110
function assemble_diffusion(rs, sts, ispcs; combinatoric_ratelaws = true,
107-
remove_conserved = false)
111+
remove_conserved = false, expand_catalyst_funs = true)
108112
# as BC species should ultimately get an equation, we include them in the noise matrix
109113
num_bcsts = count(isbc, get_unknowns(rs))
110114

@@ -120,7 +124,9 @@ function assemble_diffusion(rs, sts, ispcs; combinatoric_ratelaws = true,
120124
end
121125

122126
for (j, rx) in enumerate(get_rxs(rs))
123-
rlsqrt = sqrt(abs(oderatelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws)))
127+
rl = oderatelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws,
128+
expand_catalyst_funs)
129+
rlsqrt = sqrt(abs(rl))
124130
hasnoisescaling(rx) && (rlsqrt *= getnoisescaling(rx))
125131
remove_conserved && (rlsqrt = substitute(rlsqrt, depspec_submap))
126132

@@ -169,9 +175,12 @@ Notes:
169175
the ratelaw is `k*S*(S-1)`, i.e. the rate law is not normalized by the scaling
170176
factor.
171177
"""
172-
function jumpratelaw(rx; combinatoric_ratelaw = true)
178+
function jumpratelaw(rx; combinatoric_ratelaw = true, expand_catalyst_funs = true)
173179
@unpack rate, substrates, substoich, only_use_rate = rx
180+
174181
rl = rate
182+
expand_catalyst_funs && (rl = expand_registered_functions(rl))
183+
175184
if !only_use_rate
176185
coef = eltype(substoich) <: Number ? one(eltype(substoich)) : 1
177186
for (i, stoich) in enumerate(substoich)
@@ -313,7 +322,7 @@ function get_depgraph(rs)
313322
eqeq_dependencies(jdeps, vdeps).fadjlist
314323
end
315324

316-
function assemble_jumps(rs; combinatoric_ratelaws = true)
325+
function assemble_jumps(rs; combinatoric_ratelaws = true, expand_catalyst_funs = true)
317326
meqs = MassActionJump[]
318327
ceqs = ConstantRateJump[]
319328
veqs = VariableRateJump[]
@@ -361,7 +370,8 @@ function assemble_jumps(rs; combinatoric_ratelaws = true)
361370
if (!isvrj) && ismassaction(rx, rs; rxvars, haveivdep = false, unknownset)
362371
push!(meqs, makemajump(rx; combinatoric_ratelaw = combinatoric_ratelaws))
363372
else
364-
rl = jumpratelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws)
373+
rl = jumpratelaw(rx; combinatoric_ratelaw = combinatoric_ratelaws,
374+
expand_catalyst_funs)
365375
affect = Vector{Equation}()
366376
for (spec, stoich) in rx.netstoich
367377
# don't change species that are constant or BCs
@@ -481,12 +491,15 @@ Keyword args and default values:
481491
- `remove_conserved=false`, if set to `true` will calculate conservation laws of the
482492
underlying set of reactions (ignoring constraint equations), and then apply them to reduce
483493
the number of equations.
494+
- `expand_catalyst_funs = true`, replaces Catalyst defined functions like `hill(A,B,C,D)`
495+
with their rational function representation when converting to another system type. Set to
496+
`false`` to disable.
484497
"""
485498
function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs),
486499
combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
487-
include_zero_odes = true, remove_conserved = false,
488-
checks = false, default_u0 = Dict(), default_p = Dict(),
489-
defaults = _merge(Dict(default_u0), Dict(default_p)),
500+
include_zero_odes = true, remove_conserved = false, checks = false,
501+
default_u0 = Dict(), default_p = Dict(),
502+
defaults = _merge(Dict(default_u0), Dict(default_p)), expand_catalyst_funs = true,
490503
kwargs...)
491504
# Error checks.
492505
iscomplete(rs) || error(COMPLETENESS_ERROR)
@@ -496,7 +509,7 @@ function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs)
496509
remove_conserved && conservationlaws(fullrs)
497510
ists, ispcs = get_indep_sts(fullrs, remove_conserved)
498511
eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved,
499-
include_zero_odes)
512+
include_zero_odes, expand_catalyst_funs)
500513
eqs, us, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved)
501514

502515
ODESystem(eqs, get_iv(fullrs), us, ps;
@@ -547,13 +560,16 @@ Keyword args and default values:
547560
conservation laws. See the [FAQ
548561
entry](https://docs.sciml.ai/Catalyst/stable/faqs/#faq_remake_nonlinprob) for more
549562
details.
563+
- `expand_catalyst_funs = true`, replaces Catalyst defined functions like `hill(A,B,C,D)`
564+
with their rational function representation when converting to another system type. Set to
565+
`false`` to disable.
550566
"""
551567
function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = nameof(rs),
552568
combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
553569
remove_conserved = false, conseqs_remake_warn = true, checks = false,
554570
default_u0 = Dict(), default_p = Dict(),
555571
defaults = _merge(Dict(default_u0), Dict(default_p)),
556-
all_differentials_permitted = false, kwargs...)
572+
all_differentials_permitted = false, expand_catalyst_funs = true, kwargs...)
557573
# Error checks.
558574
iscomplete(rs) || error(COMPLETENESS_ERROR)
559575
spatial_convert_err(rs::ReactionSystem, NonlinearSystem)
@@ -565,7 +581,7 @@ function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = name
565581
remove_conserved && conservationlaws(fullrs)
566582
ists, ispcs = get_indep_sts(fullrs, remove_conserved)
567583
eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved,
568-
as_odes = false, include_zero_odes = false)
584+
as_odes = false, include_zero_odes = false, expand_catalyst_funs)
569585
eqs, us, ps, obs, defs, initeqs = addconstraints!(eqs, fullrs, ists, ispcs;
570586
remove_conserved, treat_conserved_as_eqs = true)
571587

@@ -627,11 +643,16 @@ Notes:
627643
- `remove_conserved=false`, if set to `true` will calculate conservation laws of the
628644
underlying set of reactions (ignoring constraint equations), and then apply them to reduce
629645
the number of equations.
646+
- `expand_catalyst_funs = true`, replaces Catalyst defined functions like `hill(A,B,C,D)`
647+
with their rational function representation when converting to another system type. Set to
648+
`false`` to disable.
630649
"""
631650
function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem;
632651
name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
633652
include_zero_odes = true, checks = false, remove_conserved = false,
634-
default_u0 = Dict(), default_p = Dict(), defaults = _merge(Dict(default_u0), Dict(default_p)),
653+
default_u0 = Dict(), default_p = Dict(),
654+
defaults = _merge(Dict(default_u0), Dict(default_p)),
655+
expand_catalyst_funs = true,
635656
kwargs...)
636657
# Error checks.
637658
iscomplete(rs) || error(COMPLETENESS_ERROR)
@@ -642,9 +663,9 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem;
642663
remove_conserved && conservationlaws(flatrs)
643664
ists, ispcs = get_indep_sts(flatrs, remove_conserved)
644665
eqs = assemble_drift(flatrs, ispcs; combinatoric_ratelaws, include_zero_odes,
645-
remove_conserved)
646-
noiseeqs = assemble_diffusion(flatrs, ists, ispcs;
647-
combinatoric_ratelaws, remove_conserved)
666+
remove_conserved, expand_catalyst_funs)
667+
noiseeqs = assemble_diffusion(flatrs, ists, ispcs; combinatoric_ratelaws,
668+
remove_conserved, expand_catalyst_funs)
648669
eqs, us, ps, obs, defs = addconstraints!(eqs, flatrs, ists, ispcs; remove_conserved)
649670

650671
if any(isbc, get_unknowns(flatrs))
@@ -678,12 +699,14 @@ Notes:
678699
differential equations.
679700
- Does not currently support continuous events as these are not supported by
680701
`ModelingToolkit.JumpSystems`.
702+
- `expand_catalyst_funs = true`, replaces Catalyst defined functions like `hill(A,B,C,D)`
703+
with their rational function representation when converting to another system type. Set to
704+
`false`` to disable.
681705
"""
682706
function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs),
683707
combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
684-
remove_conserved = nothing, checks = false,
685-
default_u0 = Dict(), default_p = Dict(),
686-
defaults = _merge(Dict(default_u0), Dict(default_p)),
708+
remove_conserved = nothing, checks = false, default_u0 = Dict(), default_p = Dict(),
709+
defaults = _merge(Dict(default_u0), Dict(default_p)), expand_catalyst_funs = true,
687710
kwargs...)
688711
iscomplete(rs) || error(COMPLETENESS_ERROR)
689712
spatial_convert_err(rs::ReactionSystem, JumpSystem)
@@ -696,7 +719,7 @@ function Base.convert(::Type{<:JumpSystem}, rs::ReactionSystem; name = nameof(rs
696719
(length(MT.continuous_events(flatrs)) > 0) &&
697720
(@warn "continuous_events will be dropped as they are not currently supported by JumpSystems.")
698721

699-
eqs = assemble_jumps(flatrs; combinatoric_ratelaws)
722+
eqs = assemble_jumps(flatrs; combinatoric_ratelaws, expand_catalyst_funs)
700723

701724
# handle BC species
702725
sts, ispcs = get_indep_sts(flatrs)
@@ -720,9 +743,9 @@ function DiffEqBase.ODEProblem(rs::ReactionSystem, u0, tspan,
720743
check_length = false, name = nameof(rs),
721744
combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
722745
include_zero_odes = true, remove_conserved = false, checks = false,
723-
structural_simplify = false, kwargs...)
746+
expand_catalyst_funs = true, structural_simplify = false, kwargs...)
724747
osys = convert(ODESystem, rs; name, combinatoric_ratelaws, include_zero_odes, checks,
725-
remove_conserved)
748+
remove_conserved, expand_catalyst_funs)
726749

727750
# Handles potential differential algebraic equations (which requires `structural_simplify`).
728751
if structural_simplify
@@ -764,14 +787,19 @@ Keyword args and default values:
764787
conservation laws. See the [FAQ
765788
entry](https://docs.sciml.ai/Catalyst/stable/faqs/#faq_remake_nonlinprob) for more
766789
details.
790+
- `expand_catalyst_funs = true`, replaces Catalyst defined functions like `hill(A,B,C,D)`
791+
with their rational function representation when converting to another system type. Set to
792+
`false`` to disable.
767793
"""
768794
function DiffEqBase.NonlinearProblem(rs::ReactionSystem, u0,
769795
p = DiffEqBase.NullParameters(), args...;
770796
name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
771-
remove_conserved = false, conseqs_remake_warn = true, checks = false, check_length = false,
797+
remove_conserved = false, conseqs_remake_warn = true, checks = false,
798+
check_length = false, expand_catalyst_funs = true,
772799
structural_simplify = false, all_differentials_permitted = false, kwargs...)
773800
nlsys = convert(NonlinearSystem, rs; name, combinatoric_ratelaws, checks,
774-
all_differentials_permitted, remove_conserved, conseqs_remake_warn)
801+
all_differentials_permitted, remove_conserved, conseqs_remake_warn,
802+
expand_catalyst_funs)
775803
nlsys = structural_simplify ? MT.structural_simplify(nlsys) : complete(nlsys)
776804
return NonlinearProblem(nlsys, u0, p, args...; check_length,
777805
kwargs...)
@@ -781,9 +809,10 @@ end
781809
function DiffEqBase.SDEProblem(rs::ReactionSystem, u0, tspan,
782810
p = DiffEqBase.NullParameters(), args...;
783811
name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
784-
include_zero_odes = true, checks = false, check_length = false, remove_conserved = false,
785-
structural_simplify = false, kwargs...)
786-
sde_sys = convert(SDESystem, rs; name, combinatoric_ratelaws,
812+
include_zero_odes = true, checks = false, check_length = false,
813+
remove_conserved = false, structural_simplify = false,
814+
expand_catalyst_funs = true, kwargs...)
815+
sde_sys = convert(SDESystem, rs; name, combinatoric_ratelaws, expand_catalyst_funs,
787816
include_zero_odes, checks, remove_conserved)
788817

789818
# Handles potential differential algebraic equations (which requires `structural_simplify`).
@@ -848,8 +877,9 @@ plot(sol, idxs = :A)
848877
"""
849878
function JumpInputs(rs::ReactionSystem, u0, tspan, p = DiffEqBase.NullParameters();
850879
name = nameof(rs), combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
851-
checks = false, kwargs...)
852-
jsys = complete(convert(JumpSystem, rs; name, combinatoric_ratelaws, checks))
880+
expand_catalyst_funs = true, checks = false, kwargs...)
881+
jsys = complete(convert(JumpSystem, rs; name, combinatoric_ratelaws,
882+
expand_catalyst_funs, checks))
853883
if MT.has_variableratejumps(jsys)
854884
prob = ODEProblem(jsys, u0, tspan, p; kwargs...)
855885
else
@@ -873,11 +903,11 @@ end
873903

874904
# DiscreteProblem from AbstractReactionNetwork
875905
function DiffEqBase.DiscreteProblem(rs::ReactionSystem, u0, tspan::Tuple,
876-
p = DiffEqBase.NullParameters(), args...;
877-
name = nameof(rs),
878-
combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
879-
checks = false, kwargs...)
880-
jsys = convert(JumpSystem, rs; name, combinatoric_ratelaws, checks)
906+
p = DiffEqBase.NullParameters(), args...; name = nameof(rs),
907+
combinatoric_ratelaws = get_combinatoric_ratelaws(rs), checks = false,
908+
expand_catalyst_funs = true, kwargs...)
909+
jsys = convert(JumpSystem, rs; name, combinatoric_ratelaws, checks,
910+
expand_catalyst_funs)
881911
jsys = complete(jsys)
882912
return DiscreteProblem(jsys, u0, tspan, p, args...; kwargs...)
883913
end
@@ -886,8 +916,9 @@ end
886916
function JumpProcesses.JumpProblem(rs::ReactionSystem, prob,
887917
aggregator = JumpProcesses.NullAggregator(); name = nameof(rs),
888918
combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
889-
checks = false, kwargs...)
890-
jsys = convert(JumpSystem, rs; name, combinatoric_ratelaws, checks)
919+
expand_catalyst_funs = true, checks = false, kwargs...)
920+
jsys = convert(JumpSystem, rs; name, combinatoric_ratelaws,
921+
expand_catalyst_funs, checks)
891922
jsys = complete(jsys)
892923
return JumpProblem(jsys, prob, aggregator; kwargs...)
893924
end
@@ -905,9 +936,9 @@ function DiffEqBase.SteadyStateProblem(rs::ReactionSystem, u0,
905936
check_length = false, name = nameof(rs),
906937
combinatoric_ratelaws = get_combinatoric_ratelaws(rs),
907938
remove_conserved = false, include_zero_odes = true, checks = false,
908-
structural_simplify = false, kwargs...)
939+
expand_catalyst_funs = true, structural_simplify = false, kwargs...)
909940
osys = convert(ODESystem, rs; name, combinatoric_ratelaws, include_zero_odes, checks,
910-
remove_conserved)
941+
remove_conserved, expand_catalyst_funs)
911942

912943
# Handles potential differential algebraic equations (which requires `structural_simplify`).
913944
if structural_simplify

0 commit comments

Comments
 (0)