From 4de0daa8d1d5acbbda7a64b345dabeeff66d5a5e Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 1 Feb 2024 15:19:10 -0500 Subject: [PATCH 1/4] init --- .github/workflows/CI.yml | 2 +- Project.toml | 2 +- docs/src/tutorials/creating_ode.md | 2 +- docs/src/tutorials/discrete_time.md | 2 +- ...{ModelingToolkitExt.jl => ModelingToolkitSIExt.jl} | 11 ++++------- src/StructuralIdentifiability.jl | 6 ++++++ test/extensions/modelingtoolkit.jl | 6 +++++- test/runtests.jl | 4 ++-- 8 files changed, 21 insertions(+), 14 deletions(-) rename ext/{ModelingToolkitExt.jl => ModelingToolkitSIExt.jl} (99%) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 997b5426..ed747b4c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -13,7 +13,7 @@ jobs: matrix: group: - Core - - ModelingToolkitExt + - ModelingToolkitSIExt version: - '1' - '1.6' diff --git a/Project.toml b/Project.toml index 9c061245..005a89cc 100644 --- a/Project.toml +++ b/Project.toml @@ -26,7 +26,7 @@ SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" [extensions] -ModelingToolkitExt = ["ModelingToolkit", "SymbolicUtils", "Symbolics"] +ModelingToolkitSIExt = ["ModelingToolkit", "SymbolicUtils", "Symbolics"] [compat] AbstractAlgebra = "0.34.5, 0.35" diff --git a/docs/src/tutorials/creating_ode.md b/docs/src/tutorials/creating_ode.md index 915a742a..a09b069c 100644 --- a/docs/src/tutorials/creating_ode.md +++ b/docs/src/tutorials/creating_ode.md @@ -54,7 +54,7 @@ assess_identifiability(ode) ## Defining using `ModelingToolkit` -`StructuralIdentifiability` has an extension `ModelingToolkitExt` which allows to use `ODESystem` from `ModelingToolkit` to describe +`StructuralIdentifiability` has an extension `ModelingToolkitSIExt` which allows to use `ODESystem` from `ModelingToolkit` to describe a model. The extension is loaded automatically once `ModelingToolkit` is loaded via `using ModelingToolkit`. In this case, one should encode the equations for the states as `ODESystem` and specify the outputs separately. In order to do this, we first introduce all functions and scalars: diff --git a/docs/src/tutorials/discrete_time.md b/docs/src/tutorials/discrete_time.md index c8f0571f..4138c47b 100644 --- a/docs/src/tutorials/discrete_time.md +++ b/docs/src/tutorials/discrete_time.md @@ -87,7 +87,7 @@ assess_local_identifiability(dds; funcs_to_check = [β * S]) As other main functions in the package, `assess_local_identifiability` accepts an optional parameter `loglevel` (default: `Logging.Info`) to adjust the verbosity of logging. -If one loads `ModelingToolkit` (and thus the `ModelingToolkitExt` extension), one can use `DiscreteSystem` from `ModelingToolkit` to +If one loads `ModelingToolkit` (and thus the `ModelingToolkitSIExt` extension), one can use `DiscreteSystem` from `ModelingToolkit` to describe the input model (now in terms of difference!): ```@example discrete_mtk diff --git a/ext/ModelingToolkitExt.jl b/ext/ModelingToolkitSIExt.jl similarity index 99% rename from ext/ModelingToolkitExt.jl rename to ext/ModelingToolkitSIExt.jl index 33447508..65af94c9 100644 --- a/ext/ModelingToolkitExt.jl +++ b/ext/ModelingToolkitSIExt.jl @@ -1,4 +1,4 @@ -module ModelingToolkitExt +module ModelingToolkitSIExt using DataStructures using Logging @@ -15,9 +15,6 @@ else using ..ModelingToolkit end -export mtk_to_si -export assess_local_identifiability, assess_identifiability, find_identifiable_functions - # ------------------------------------------------------------------------------ function eval_at_nemo(e::Num, vals::Dict) @@ -96,7 +93,7 @@ Output: - `conversion` dictionary from the symbols in the input MTK model to the variable involved in the produced `ODE` object """ -function mtk_to_si( +function StructuralIdentifiability.mtk_to_si( de::ModelingToolkit.AbstractTimeDependentSystem, measured_quantities::Array{ModelingToolkit.Equation}, ) @@ -106,7 +103,7 @@ function mtk_to_si( ) end -function mtk_to_si( +function StructuralIdentifiability.mtk_to_si( de::ModelingToolkit.AbstractTimeDependentSystem, measured_quantities::Array{<:Symbolics.Num}, ) @@ -116,7 +113,7 @@ function mtk_to_si( ) end -function mtk_to_si( +function StructuralIdentifiability.mtk_to_si( de::ModelingToolkit.AbstractTimeDependentSystem, measured_quantities::Array{<:SymbolicUtils.BasicSymbolic}, ) diff --git a/src/StructuralIdentifiability.jl b/src/StructuralIdentifiability.jl index f5a8cb8e..66713cca 100644 --- a/src/StructuralIdentifiability.jl +++ b/src/StructuralIdentifiability.jl @@ -172,4 +172,10 @@ end using PrecompileTools include("precompile.jl") +### Extensions ### + +# ModelingToolkit extension. +function mtk_to_si end +export mtk_to_si + end diff --git a/test/extensions/modelingtoolkit.jl b/test/extensions/modelingtoolkit.jl index 7753e565..c85883a6 100644 --- a/test/extensions/modelingtoolkit.jl +++ b/test/extensions/modelingtoolkit.jl @@ -1,4 +1,4 @@ -if GROUP == "All" || GROUP == "ModelingToolkitExt" +if GROUP == "All" || GROUP == "ModelingToolkitSIExt" @testset "Check identifiability of `ODESystem` object" begin using ModelingToolkit using ModelingToolkit: parameters @@ -656,4 +656,8 @@ if GROUP == "All" || GROUP == "ModelingToolkitExt" ) == c[:res] end end + + @testset "Exporting ModelingToolkit Model to SI Model" begin + + end end diff --git a/test/runtests.jl b/test/runtests.jl index 1c75f517..e5ed9fe7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -68,7 +68,7 @@ using StructuralIdentifiability: const GROUP = get(ENV, "GROUP", "All") @static if VERSION >= v"1.10.0" - if GROUP == "All" || GROUP == "ModelingToolkitExt" + if GROUP == "All" || GROUP == "ModelingToolkitSIExt" using Pkg Pkg.add("ModelingToolkit") Pkg.add("Symbolics") @@ -123,7 +123,7 @@ function get_test_files(group) if group == "All" || (group == "Core" && dir != "./extensions") || ( - group == "ModelingToolkitExt" && + group == "ModelingToolkitSIExt" && dir == "./extensions" && VERSION >= v"1.10.0" ) From 302356eb4921e9953f7dc40bd7c927b036d1cb6f Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 1 Feb 2024 15:29:45 -0500 Subject: [PATCH 2/4] prepare tests --- test/extensions/modelingtoolkit.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/extensions/modelingtoolkit.jl b/test/extensions/modelingtoolkit.jl index c85883a6..d09ce810 100644 --- a/test/extensions/modelingtoolkit.jl +++ b/test/extensions/modelingtoolkit.jl @@ -658,6 +658,6 @@ if GROUP == "All" || GROUP == "ModelingToolkitSIExt" end @testset "Exporting ModelingToolkit Model to SI Model" begin - + # Add test of `mtk_to_si` function here, as well as identifiability functions when applied to its output. end end From 8ee49c8907286066c2688bf11b41a9c1f398e19c Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 1 Feb 2024 15:50:20 -0500 Subject: [PATCH 3/4] add tests --- test/extensions/modelingtoolkit.jl | 39 +++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/test/extensions/modelingtoolkit.jl b/test/extensions/modelingtoolkit.jl index d09ce810..d06257e5 100644 --- a/test/extensions/modelingtoolkit.jl +++ b/test/extensions/modelingtoolkit.jl @@ -658,6 +658,43 @@ if GROUP == "All" || GROUP == "ModelingToolkitSIExt" end @testset "Exporting ModelingToolkit Model to SI Model" begin - # Add test of `mtk_to_si` function here, as well as identifiability functions when applied to its output. + + # Creates MTK model and assesses its identifiability. + @parameters r1, r2, c1, c2, beta1, beta2, chi1, chi2 + @variables t, x1(t), x2(t), y(t), u(t) + D= Differential(t) + eqs = [ + D(x1) ~ r1 * x1 * (1 - c1 * x1) + beta1 * x1 * x2 / (chi1 + x2) + u, + D(x2) ~ r2 * x2 * (1 - c2 * x2) + beta2 * x1 * x2 / (chi2 + x1), + ] + measured_quantities = [y ~ x1] + ode_mtk = ODESystem(eqs, t, name = :mutualist) + + global_id_1 = assess_identifiability(ode_mtk, measured_quantities = measured_quantities) + local_id_1 = assess_local_identifiability(ode_mtk, measured_quantities = measured_quantities) + ifs_1 = find_identifiable_functions(ode_mtk, measured_quantities = measured_quantities) + + # Converts mtk model to si model, and assesses its identifiability. + si_model, _ = mtk_to_si(ode_mtk, measured_quantities) + global_id_2 = assess_identifiability(si_model) + local_id_2 = assess_local_identifiability(si_model) + ifs_2 = find_identifiable_functions(si_model) + + # Converts the output dicts from StructuralIdentifiability functions from "weird symbol => stuff" to "symbol => stuff" (the output have some strange meta data which prevents equality checks, this enables this). + # Structural identifiability also provides variables like x (rather than x(t)). This is a bug, but we have to convert to make it work (now just remove any (t) to make them all equal). + function sym_dict(dict_in) + dict_out = Dict{Symbol,Any}() + for key in keys(dict_in) + sym_key = Symbol(key) + sym_key = Symbol(replace(String(sym_key), "(t)" => "")) + dict_out[sym_key] = dict_in[key] + end + return dict_out + end + + # Checks that the two approaches yields the same result + @test issetequal(sym_dict(local_id_1), sym_dict(local_id_2)) + @test issetequal(sym_dict(local_id_1), sym_dict(local_id_2)) + @test length(ifs_1) == length(ifs_2) end end From 18d36479ee2b407db90514ff253f85788ab50fb9 Mon Sep 17 00:00:00 2001 From: Torkel Date: Thu, 1 Feb 2024 16:56:29 -0500 Subject: [PATCH 4/4] format tests --- test/extensions/modelingtoolkit.jl | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/extensions/modelingtoolkit.jl b/test/extensions/modelingtoolkit.jl index d06257e5..bdeaf619 100644 --- a/test/extensions/modelingtoolkit.jl +++ b/test/extensions/modelingtoolkit.jl @@ -662,7 +662,7 @@ if GROUP == "All" || GROUP == "ModelingToolkitSIExt" # Creates MTK model and assesses its identifiability. @parameters r1, r2, c1, c2, beta1, beta2, chi1, chi2 @variables t, x1(t), x2(t), y(t), u(t) - D= Differential(t) + D = Differential(t) eqs = [ D(x1) ~ r1 * x1 * (1 - c1 * x1) + beta1 * x1 * x2 / (chi1 + x2) + u, D(x2) ~ r2 * x2 * (1 - c2 * x2) + beta2 * x1 * x2 / (chi2 + x1), @@ -670,9 +670,12 @@ if GROUP == "All" || GROUP == "ModelingToolkitSIExt" measured_quantities = [y ~ x1] ode_mtk = ODESystem(eqs, t, name = :mutualist) - global_id_1 = assess_identifiability(ode_mtk, measured_quantities = measured_quantities) - local_id_1 = assess_local_identifiability(ode_mtk, measured_quantities = measured_quantities) - ifs_1 = find_identifiable_functions(ode_mtk, measured_quantities = measured_quantities) + global_id_1 = + assess_identifiability(ode_mtk, measured_quantities = measured_quantities) + local_id_1 = + assess_local_identifiability(ode_mtk, measured_quantities = measured_quantities) + ifs_1 = + find_identifiable_functions(ode_mtk, measured_quantities = measured_quantities) # Converts mtk model to si model, and assesses its identifiability. si_model, _ = mtk_to_si(ode_mtk, measured_quantities) @@ -683,18 +686,18 @@ if GROUP == "All" || GROUP == "ModelingToolkitSIExt" # Converts the output dicts from StructuralIdentifiability functions from "weird symbol => stuff" to "symbol => stuff" (the output have some strange meta data which prevents equality checks, this enables this). # Structural identifiability also provides variables like x (rather than x(t)). This is a bug, but we have to convert to make it work (now just remove any (t) to make them all equal). function sym_dict(dict_in) - dict_out = Dict{Symbol,Any}() + dict_out = Dict{Symbol, Any}() for key in keys(dict_in) sym_key = Symbol(key) sym_key = Symbol(replace(String(sym_key), "(t)" => "")) dict_out[sym_key] = dict_in[key] - end + end return dict_out end # Checks that the two approaches yields the same result @test issetequal(sym_dict(local_id_1), sym_dict(local_id_2)) @test issetequal(sym_dict(local_id_1), sym_dict(local_id_2)) - @test length(ifs_1) == length(ifs_2) + @test length(ifs_1) == length(ifs_2) end end