-
-
Notifications
You must be signed in to change notification settings - Fork 80
Description
Is your feature request related to a problem? Please describe.
The current modularization methods are ambiguous and produce unneeded parameters. This is a complex issue and the point of this post is to capture some thoughts on how a bottom-up approach might help. The tables below will show the scope of information that needs to be handled in QSP or PBPK models.
Let's start with an example from the docs:
using Catalyst
function repressed_gene(; R, name)
@network_component $name begin
hillr($R,α,K,n), ∅ --> m
(δ,γ), m <--> ∅
β, m --> m + P
μ, P --> ∅
end
end
t = default_t()
@species G3₊P(t)
@named G1 = repressed_gene(; R=ParentScope(G3₊P))
@named G2 = repressed_gene(; R=ParentScope(G1.P))
@named G3 = repressed_gene(; R=ParentScope(G2.P))
@named repressilator = ReactionSystem(t; systems=[G1,G2,G3])
@show parameters(repressilator)
t = default_t()
@species P₁(t) P₂(t) P₃(t)
rxs = [(@reaction hillr($P₃,α,K,n), ∅ --> m₁),
(@reaction hillr($P₁,α,K,n), ∅ --> m₂),
(@reaction hillr($P₂,α,K,n), ∅ --> m₃),
(@reaction δ, m₁ --> ∅),
(@reaction γ, ∅ --> m₁),
(@reaction δ, m₂ --> ∅),
(@reaction γ, ∅ --> m₂),
(@reaction δ, m₃ --> ∅),
(@reaction γ, ∅ --> m₃),
(@reaction β, m₁ --> m₁ + P₁),
(@reaction β, m₂ --> m₂ + P₂),
(@reaction β, m₃ --> m₃ + P₃),
(@reaction μ, P₁ --> ∅),
(@reaction μ, P₂ --> ∅),
(@reaction μ, P₃ --> ∅)]
@named repressilator2 = ReactionSystem(rxs, t)
@show parameters(repressilator2)
produces 21 parameters in the modular method and 7 in the standard method:
parameters(repressilator) = Any[G1₊α, G1₊K, G1₊n, G1₊δ, G1₊γ, G1₊β, G1₊μ, G2₊α, G2₊K, G2₊n, G2₊δ, G2₊γ, G2₊β, G2₊μ, G3₊α, G3₊K, G3₊n, G3₊δ, G3₊γ, G3₊β, G3₊μ]
parameters(repressilator2) = Any[α, n, K, δ, γ, β, μ]
Describe the solution you’d like
Rather than removing redundant parameters posthoc, it would be better to never have them in the model. This requires a way to designate when a parameter is species-specific.
There are a large number of edge cases in QSP, PBPK, etc. that are difficult to anticipate. However, I think the tables below capture many of these cases. To implement a bottom up strategy, the wildcard character '@#' is replaced by building blocks that are used to construct equations and parameters via loops over building_blocks entries. I show the repressilator and a more complex PBPK model with flows, compartments and isotopes.
reaction_table = [
# Reactant, Product, Rate_type, Rate_eqtn_prototype, Building_blocks, Isotopes Compartment
(None, "m_@1", "HillR", ["P_@2","alpha","K","n"], [["1","3"],["2","1"],["3","2"]], None, None),
("m@1", None, "mass_action", "delta", ["1","2","3"], None, None),
("m@1", ["m@1","P_@1"], "mass_action", "beta", ["1","2","3"], None, None),
("P_@1", None, "mass_action", "mu", ["1","2","3"], None, None)
]
reaction_table = [
# Reactant, Product, Rate_type, Rate_eqtn_prototype, Building_blocks, Isotopes Compartment
("Leu", "APP", "mass_action", "k_f", None, ["U", "L"], ["ISF"]),
("APP", "C99", "MM", ["V_M2","K_M2"], None, ["U", "L"], ["ISF"]),
("APP", "C83", "MM", ["V_M1","K_M1","CI:[C99,KM5]"], None, ["U", "L"], ["ISF"]),
("C99", "@1", "MM", ["V_gamma_@1","K_M4","CI:[C83,KM3]"], None, ["U", "L"], ["ISF"]),
("C99", "C83", "MM", ["V_M5","K_M5","CI:[APP,KM1]"], None, ["U", "L"], ["ISF"]),
("C83", None, "MM", ["V_M3","K_M3","CI:[C99,KM4]"], None, ["U", "L"], ["ISF"]),
("@1", "@1_ex", "reversible_mass_action", ["k_ex_@1","k_ret_@1"], ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["ISF","ISF"]),
("@1", None, "mass_action", "k_BPD_@1", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["ISF"]),
("@1", "@1_cranial", "bidirectional_flow", "Q_glymph", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["ISF","cranial"]),
("@1_cranial", "@1_CV", "bidirectional_flow", "Q_osc", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["cranial","CV"]),
("@1_cranial", None, "unidirectional_flow", "Q_cranial", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["cranial","0"]),
("@1_CV", "@1_cranial", "unidirectional_flow", "Q_cranial", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["CV","cranial"]),
("@1_CV", "@1_SP1", "unidirectional_flow", "Q_LP", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["CV","SP1"]),
("@1_SP1", "@1_SP2", "unidirectional_flow", "Q_LP", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP1","SP2"]),
("@1_SP2", "@1_SP3", "unidirectional_flow", "Q_LP", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP2","SP3"]),
("@1_SP3", None, "unidirectional_flow", "Q_LP", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP3","0"]),
("@1_CV", "@1_SP1", "unidirectional_flow", "Q_SN1", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["CV","SP1"]),
("@1_SP1", "@1_SP2", "unidirectional_flow", "Q_SN2", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP1","SP2"]),
("@1_SP1", None, "unidirectional_flow", "Q_SN3", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP1","0"]),
("@1_SP2", "@1_SP3", "unidirectional_flow", "Q_SN3", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP2","SP3"]),
("@1_SP2", None, "unidirectional_flow", "Q_SN3", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP2","0"]),
("@1_SP3", None, "unidirectional_flow", "Q_SN3", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP3","0"]),
("@1_CV", "@1_SP1", "bidirectional_flow", "Q_osc", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["CV","SP1"]),
("@1_SP1", "@1_SP2", "bidirectional_flow", "Q_osc", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP1","SP2"]),
("@1_SP2", "@1_SP3", "bidirectional_flow", "Q_osc", ["Ab38", "Ab40", "Ab42"], ["U", "L"], ["SP2","SP3"])
]
Isotopes are interesting because they should have identical kinetics, so they should be handled separately from the Building_blocks. Compartments need some nomenclature to distinguish reactions within a compartment and reactions (or flows) that cause movement between compartments.
Conceptually, what is in the tables above is not that different from normal Catalyst. In fact I think Catalyst could be written like this:
function repressed_gene(; R, m, P, name)
@network_component $name begin
hillr($R,α,K,n), ∅ --> $m
(δ,γ), $m <--> ∅
β, $m --> $m + $P
μ, $P --> ∅
end
end
where the default is that parameters are NOT species-specific unless designated with some wildcard (not shown here) but could be something like δ_$m.
Describe alternatives you’ve considered
Antimony has the same behavior when constructing modular models.