Skip to content

Commit 8a1baf6

Browse files
committed
Fixes extension support, add more test, fix conversion bug
1 parent bd00bd2 commit 8a1baf6

File tree

8 files changed

+319
-22
lines changed

8 files changed

+319
-22
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ version = "13.5.1"
66
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
77
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
88
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
9+
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
910
DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821"
1011
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
1112
JumpProcesses = "ccbc3e58-028d-4f4c-8cd5-9ae44345cda5"

ext/CatalystHomotopyContinuationExtension.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module CatalystHomotopyContinuationExtension
22

33
# Fetch packages.
44
using Catalyst
5+
import DynamicPolynomials
56
import ModelingToolkit as MT
67
import HomotopyContinuation as HC
78
import Setfield: @set

ext/CatalystHomotopyContinuationExtension/homotopy_continuation_extension.jl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ function steady_state_polynomial(rs::ReactionSystem, ps, u0)
5353
eqs_pars_funcs = vcat(equations(ns), conservedequations(rs))
5454
eqs = map(eq -> substitute(eq.rhs - eq.lhs, p_dict), eqs_pars_funcs)
5555
eqs_intexp = make_int_exps.(eqs)
56-
return Catalyst.to_multivariate_poly(remove_denominators.(eqs_intexp))
56+
ss_poly = Catalyst.to_multivariate_poly(remove_denominators.(eqs_intexp))
57+
return poly_type_convert(ss_poly)
5758
end
5859

5960
# If u0s are not given while conservation laws are present, throws an error.
@@ -94,7 +95,7 @@ end
9495
function reorder_sols!(sols, ss_poly, rs::ReactionSystem)
9596
var_names_extended = String.(Symbol.(HC.variables(ss_poly)))
9697
var_names = [Symbol(s[1:prevind(s, findlast('_', s))]) for s in var_names_extended]
97-
sort_pattern = indexin(MT.getname.(species(rs)), var_names)
98+
sort_pattern = indexin(MT.getname.(unknowns(rs)), var_names)
9899
foreach(sol -> permute!(sol, sort_pattern), sols)
99100
end
100101

@@ -104,4 +105,13 @@ function filter_negative_f(sols; neg_thres=-1e-20)
104105
(neg_thres < sol[idx] < 0) && (sol[idx] = 0)
105106
end
106107
return filter(sol -> all(>=(0), sol), sols)
108+
end
109+
110+
# Sometimes (when polynomials are created from hybrid CRN/DAEs), the steady state polynomial have the wrong type.
111+
# This converts it to the correct type, which homotopy continuation can handle.
112+
const WRONG_POLY_TYPE = Vector{DynamicPolynomials.Polynomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}}}
113+
const CORRECT_POLY_TYPE = Vector{DynamicPolynomials.Polynomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, DynamicPolynomials.Graded{DynamicPolynomials.LexOrder}, Float64}}
114+
function poly_type_convert(ss_poly)
115+
(typeof(ss_poly) == WRONG_POLY_TYPE) && return convert(CORRECT_POLY_TYPE, ss_poly)
116+
return ss_poly
107117
end

src/reactionsystem.jl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,7 @@ function get_indep_sts(rs::ReactionSystem, remove_conserved = false)
993993
sts = get_unknowns(rs)
994994
nps = get_networkproperties(rs)
995995
indepsts = if remove_conserved
996-
filter(s -> (s nps.indepspecs) && (!isbc(s)), sts)
996+
filter(s -> ((s nps.indepspecs) || (!isspecies(s))) && (!isbc(s)), sts)
997997
else
998998
filter(s -> !isbc(s), sts)
999999
end
@@ -1573,9 +1573,9 @@ function Base.convert(::Type{<:ODESystem}, rs::ReactionSystem; name = nameof(rs)
15731573
ists, ispcs = get_indep_sts(fullrs, remove_conserved)
15741574
eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved,
15751575
include_zero_odes)
1576-
eqs, sts, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved, zero_derivatives=true)
1576+
eqs, us, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved, zero_derivatives=true)
15771577

1578-
ODESystem(eqs, get_iv(fullrs), sts, ps;
1578+
ODESystem(eqs, get_iv(fullrs), us, ps;
15791579
observed = obs,
15801580
name,
15811581
defaults = _merge(defaults,defs),
@@ -1614,10 +1614,10 @@ function Base.convert(::Type{<:NonlinearSystem}, rs::ReactionSystem; name = name
16141614
ists, ispcs = get_indep_sts(fullrs, remove_conserved)
16151615
eqs = assemble_drift(fullrs, ispcs; combinatoric_ratelaws, remove_conserved,
16161616
as_odes = false, include_zero_odes)
1617-
eqs, sts, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved)
1617+
eqs, us, ps, obs, defs = addconstraints!(eqs, fullrs, ists, ispcs; remove_conserved)
16181618
eqs = [remove_diffs(eq.lhs) ~ remove_diffs(eq.rhs) for eq in eqs]
16191619

1620-
NonlinearSystem(eqs, sts, ps;
1620+
NonlinearSystem(eqs, us, ps;
16211621
name,
16221622
observed = obs,
16231623
defaults = _merge(defaults,defs),
@@ -1669,13 +1669,13 @@ function Base.convert(::Type{<:SDESystem}, rs::ReactionSystem;
16691669
eqs = assemble_drift(flatrs, ispcs; combinatoric_ratelaws, include_zero_odes,
16701670
remove_conserved)
16711671
noiseeqs = assemble_diffusion(flatrs, ists, ispcs; combinatoric_ratelaws, remove_conserved)
1672-
eqs, sts, ps, obs, defs = addconstraints!(eqs, flatrs, ists, ispcs; remove_conserved)
1672+
eqs, us, ps, obs, defs = addconstraints!(eqs, flatrs, ists, ispcs; remove_conserved)
16731673

16741674
if any(isbc, get_unknowns(flatrs))
16751675
@info "Boundary condition species detected. As constraint equations are not currently supported when converting to SDESystems, the resulting system will be undetermined. Consider using constant species instead."
16761676
end
16771677

1678-
SDESystem(eqs, noiseeqs, get_iv(flatrs), sts, ps;
1678+
SDESystem(eqs, noiseeqs, get_iv(flatrs), us, ps;
16791679
observed = obs,
16801680
name,
16811681
defaults = defs,

test/extensions/bifurcation_kit.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,29 @@ end
180180

181181
### Other Tests ###
182182

183+
# Checks that bifurcation diagrams can be computed for hybrid CRN/DAE systems.
184+
let
185+
# Prepares the model (production/degradation of X, with equations for volume and X concentration).
186+
rs = @reaction_network begin
187+
@parameters k
188+
@variables C(t)
189+
@equations begin
190+
D(V) ~ k*X - V
191+
C ~ X/V
192+
end
193+
(p/V,d/V), 0 <--> X
194+
end
195+
u0_guess = [:X => 1.0, :V => 1.0, :C => 1.0]
196+
p_start = [:p => 2.0, :d => 1.0, :k => 5.0]
197+
198+
# Computes bifurcation diagram.
199+
bprob = BifurcationProblem(rs, u0_guess, p_start, :p; plot_var = :C)
200+
p_span = (0.1, 6.0)
201+
opts_br = ContinuationPar(dsmin = 0.0001, dsmax = 0.001, ds = 0.0001, max_steps = 10000, p_min = p_span[1], p_max = p_span[2], n_inversion = 4)
202+
bif_dia = bifurcationdiagram(bprob, PALC(), 2, (args...) -> opts_br; bothside = true)
203+
@test all(getfield.(bif_dia.γ.branch, :x) .≈ 0.2)
204+
end
205+
183206
# Checks that `BifurcationProblem`s cannot be generated from non-complete `ReactionSystems`s.
184207
let
185208
# Create model.

test/extensions/homotopy_continuation.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,25 @@ end
109109

110110
### Other Tests ###
111111

112+
# Tests that homotopy continuation can be applied to hybrid DAE/CRN systems.
113+
let
114+
# Prepares the model (production/degradation of X, with equations for volume and X concentration).
115+
rs = @reaction_network begin
116+
@parameters k
117+
@variables C(t)
118+
@equations begin
119+
D(V) ~ k*X - V
120+
C ~ X/V
121+
end
122+
(p/V,d/V), 0 <--> X
123+
end
124+
125+
# Checks that homotopy continuation correctly find the system's single steady state.
126+
ps = [:p => 2.0, :d => 1.0, :k => 5.0]
127+
hc_ss = hc_steady_states(rs, ps)
128+
@test hc_ss [[2.0, 0.2, 10.0]]
129+
end
130+
112131
# Checks that `hc_steady_states` cannot be applied to non-complete `ReactionSystems`s.
113132
let
114133
# Create model.

test/extensions/structural_identifiability.jl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,39 @@ end
312312

313313
### Other Tests ###
314314

315+
# Checks that identifiability can be assessed for hybrid CRN/DAE systems.
316+
let
317+
rs = @reaction_network begin
318+
@parameters k c1 c2
319+
@variables C(t)
320+
@equations begin
321+
D(V) ~ k*X - V
322+
C ~ (c1 + c2) * X/V
323+
end
324+
(p/V,d/V), 0 <--> X
325+
end
326+
@unpack p, d, k, c1, c2 = rs
327+
328+
# Tests identifiability assessment when all unknowns are measured.
329+
gi_1 = assess_identifiability(rs; measured_quantities=[:X, :V, :C])
330+
li_1 = assess_local_identifiability(rs; measured_quantities=[:X, :V, :C])
331+
ifs_1 = find_identifiable_functions(rs; measured_quantities=[:X, :V, :C])
332+
@test sym_dict(gi_1) == Dict([:X => :globally, :C => :globally, :V => :globally, :k => :globally,
333+
:c1 => :nonidentifiable, :c2 => :nonidentifiable, :p => :globally, :d => :globally])
334+
@test sym_dict(li_1) == Dict([:X => 1, :C => 1, :V => 1, :k => 1, :c1 => 0, :c2 => 0, :p => 1, :d => 1])
335+
@test issetequal(ifs_1, [d, p, k, c1 + c2])
336+
337+
# Tests identifiability assessment when only variables are measured.
338+
# Checks that a parameter in an equation can be set as known.
339+
gi_2 = assess_identifiability(rs; measured_quantities=[:V, :C], known_p = [:c1])
340+
li_2 = assess_local_identifiability(rs; measured_quantities=[:V, :C], known_p = [:c1])
341+
ifs_2 = find_identifiable_functions(rs; measured_quantities=[:V, :C], known_p = [:c1])
342+
@test sym_dict(gi_2) == Dict([:X => :nonidentifiable, :C => :globally, :V => :globally, :k => :nonidentifiable,
343+
:c1 => :globally, :c2 => :nonidentifiable, :p => :nonidentifiable, :d => :globally])
344+
@test sym_dict(li_2) == Dict([:X => 0, :C => 1, :V => 1, :k => 0, :c1 => 1, :c2 => 0, :p => 0, :d => 1])
345+
@test issetequal(ifs_2, [d, c1, k*p, c1*p + c2*p])
346+
end
347+
315348
# Checks that identifiability functions cannot be applied to non-complete `ReactionSystems`s.
316349
let
317350
# Create model.

0 commit comments

Comments
 (0)