diff --git a/test/RationalFunctionFields/RationalFunctionField.jl b/test/RationalFunctionFields/RationalFunctionField.jl index 9a9f88ba..e63afe14 100644 --- a/test/RationalFunctionFields/RationalFunctionField.jl +++ b/test/RationalFunctionFields/RationalFunctionField.jl @@ -1,38 +1,36 @@ -if GROUP == "All" || GROUP == "Core" - @testset "RationalFunctionField" begin - p = 0.99 - R, (a, b, c) = QQ["a", "b", "c"] +@testset "RationalFunctionField" begin + p = 0.99 + R, (a, b, c) = QQ["a", "b", "c"] - f1 = [a, b, a + b + c] - f2 = [2c, 3b, 5a] - rff1 = StructuralIdentifiability.RationalFunctionField(f1) - rff2 = StructuralIdentifiability.RationalFunctionField(f2) - @test StructuralIdentifiability.fields_equal(rff1, rff2, p) - @test all(StructuralIdentifiability.field_contains(rff1, [zero(R), one(R)], p)) - @test all(StructuralIdentifiability.field_contains(rff1, [7a, 9b, 11c], p)) - @test all(StructuralIdentifiability.field_contains(rff2, [7a, 9b, 11c], p)) + f1 = [a, b, a + b + c] + f2 = [2c, 3b, 5a] + rff1 = StructuralIdentifiability.RationalFunctionField(f1) + rff2 = StructuralIdentifiability.RationalFunctionField(f2) + @test StructuralIdentifiability.fields_equal(rff1, rff2, p) + @test all(StructuralIdentifiability.field_contains(rff1, [zero(R), one(R)], p)) + @test all(StructuralIdentifiability.field_contains(rff1, [7a, 9b, 11c], p)) + @test all(StructuralIdentifiability.field_contains(rff2, [7a, 9b, 11c], p)) - s1, s2 = a + b + c, a^2 + b^2 + c^2 - f1 = [s1, s2] - rff1 = StructuralIdentifiability.RationalFunctionField(f1) - @test !any( - StructuralIdentifiability.field_contains(rff1, [a, b + c, a * b + b * c], p), - ) - @test all( - StructuralIdentifiability.field_contains(rff1, [a * b + b * c + a * c], p), - ) - @test all(StructuralIdentifiability.field_contains(rff1, [(s1)^8 - (s2)^9 + 89], p)) + s1, s2 = a + b + c, a^2 + b^2 + c^2 + f1 = [s1, s2] + rff1 = StructuralIdentifiability.RationalFunctionField(f1) + @test !any( + StructuralIdentifiability.field_contains(rff1, [a, b + c, a * b + b * c], p), + ) + @test all( + StructuralIdentifiability.field_contains(rff1, [a * b + b * c + a * c], p), + ) + @test all(StructuralIdentifiability.field_contains(rff1, [(s1)^8 - (s2)^9 + 89], p)) - # Example in Section 5 from - # https://mediatum.ub.tum.de/doc/685465/685465.pdf - R, (x1, x2) = QQ["x1", "x2"] - g1 = (x1^3 + x1 * x2 - 2) // (x1^2 - x2 - 1) - g2 = (x1^2 + x1^2 * x2 + 7) // (x1 - x1^2 * x2^2) - g3 = x1^2 + 3x1 * x2 - g4 = x1 * x2^2 + 5x1 * x2 - g5 = x1^3 * x2 - x2 - rff1 = StructuralIdentifiability.RationalFunctionField([g1, g2, g3, g4, g5]) - rff2 = StructuralIdentifiability.RationalFunctionField([x1, x2]) - @test StructuralIdentifiability.fields_equal(rff1, rff2, p) - end + # Example in Section 5 from + # https://mediatum.ub.tum.de/doc/685465/685465.pdf + R, (x1, x2) = QQ["x1", "x2"] + g1 = (x1^3 + x1 * x2 - 2) // (x1^2 - x2 - 1) + g2 = (x1^2 + x1^2 * x2 + 7) // (x1 - x1^2 * x2^2) + g3 = x1^2 + 3x1 * x2 + g4 = x1 * x2^2 + 5x1 * x2 + g5 = x1^3 * x2 - x2 + rff1 = StructuralIdentifiability.RationalFunctionField([g1, g2, g3, g4, g5]) + rff2 = StructuralIdentifiability.RationalFunctionField([x1, x2]) + @test StructuralIdentifiability.fields_equal(rff1, rff2, p) end diff --git a/test/RationalFunctionFields/normalforms.jl b/test/RationalFunctionFields/normalforms.jl index 40381e74..a6635b66 100644 --- a/test/RationalFunctionFields/normalforms.jl +++ b/test/RationalFunctionFields/normalforms.jl @@ -1,83 +1,81 @@ -if GROUP == "All" || GROUP == "Core" - eq_up_to_the_order(a, b) = issubset(a, b) && issubset(b, a) +eq_up_to_the_order(a, b) = issubset(a, b) && issubset(b, a) - @testset "Linear relations over the rationals" begin - R, (a, b, c) = QQ["a", "b", "c"] +@testset "Linear relations over the rationals" begin + R, (a, b, c) = QQ["a", "b", "c"] - f = [a + 9] - rff = StructuralIdentifiability.RationalFunctionField(f) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) - @test eq_up_to_the_order(relations, [a]) + f = [a + 9] + rff = StructuralIdentifiability.RationalFunctionField(f) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) + @test eq_up_to_the_order(relations, [a]) - f = [a * b // R(1), (b * c + a * b) // (a * b)] - rff = StructuralIdentifiability.RationalFunctionField(f) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) - @test eq_up_to_the_order(relations, [a * b // R(1), b * c // R(1)]) + f = [a * b // R(1), (b * c + a * b) // (a * b)] + rff = StructuralIdentifiability.RationalFunctionField(f) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) + @test eq_up_to_the_order(relations, [a * b // R(1), b * c // R(1)]) - R, (a, b, c) = QQ["a", "b", "c"] - f = [a^2 + b^2, a^3 + b^3, a^4 + b^4] - rff = StructuralIdentifiability.RationalFunctionField(f) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 1) - @test eq_up_to_the_order(relations, [a + b]) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) - @test eq_up_to_the_order(relations, [a + b, a * b, a^2 + b^2]) + R, (a, b, c) = QQ["a", "b", "c"] + f = [a^2 + b^2, a^3 + b^3, a^4 + b^4] + rff = StructuralIdentifiability.RationalFunctionField(f) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 1) + @test eq_up_to_the_order(relations, [a + b]) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) + @test eq_up_to_the_order(relations, [a + b, a * b, a^2 + b^2]) - f = [9a^7 + 10b^6, b^10 - 5b^2] - rff = StructuralIdentifiability.RationalFunctionField(f) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 1) - @test eq_up_to_the_order(relations, empty(f)) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 7) - @test eq_up_to_the_order(relations, [a^7 + (10 // 9) * b^6]) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 12) - @test eq_up_to_the_order(relations, [a^7 + (10 // 9) * b^6, b^10 - 5b^2]) + f = [9a^7 + 10b^6, b^10 - 5b^2] + rff = StructuralIdentifiability.RationalFunctionField(f) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 1) + @test eq_up_to_the_order(relations, empty(f)) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 7) + @test eq_up_to_the_order(relations, [a^7 + (10 // 9) * b^6]) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 12) + @test eq_up_to_the_order(relations, [a^7 + (10 // 9) * b^6, b^10 - 5b^2]) - # Regression tests - ### - # LV model. - R, (x1, p2, p4, y1, x2, x3, u, p1, p3) = - QQ["x1", "p2", "p4", "y1", "x2", "x3", "u", "p1", "p3"] - f = [ - x3 // one(R), - x2 * x1 // one(R), - p1 * p3 // one(R), - p2 * p4 // one(R), - p1 + p3 // one(R), - (p2 * x2 + p4 * x1) // (x2 * x1), - (p2 * x2 - p4 * x1) // (p1 - p3), - ] - rff = StructuralIdentifiability.RationalFunctionField(f) - relations = StructuralIdentifiability.monomial_generators_up_to_degree( - rff, - 2, - strategy = :monte_carlo, - ) - @test (x1 * p4 + p2 * x2) // one(R) in relations + # Regression tests + ### + # LV model. + R, (x1, p2, p4, y1, x2, x3, u, p1, p3) = + QQ["x1", "p2", "p4", "y1", "x2", "x3", "u", "p1", "p3"] + f = [ + x3 // one(R), + x2 * x1 // one(R), + p1 * p3 // one(R), + p2 * p4 // one(R), + p1 + p3 // one(R), + (p2 * x2 + p4 * x1) // (x2 * x1), + (p2 * x2 - p4 * x1) // (p1 - p3), + ] + rff = StructuralIdentifiability.RationalFunctionField(f) + relations = StructuralIdentifiability.monomial_generators_up_to_degree( + rff, + 2, + strategy = :monte_carlo, + ) + @test (x1 * p4 + p2 * x2) // one(R) in relations - ### - R, (a, b, c) = QQ["a", "b", "c"] - f = [a, a * b + b * c] - rff = StructuralIdentifiability.RationalFunctionField(f) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) - @test eq_up_to_the_order(relations, [a, a * b + b * c]) + ### + R, (a, b, c) = QQ["a", "b", "c"] + f = [a, a * b + b * c] + rff = StructuralIdentifiability.RationalFunctionField(f) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) + @test eq_up_to_the_order(relations, [a, a * b + b * c]) - ### - # Some arbitrary generators for the SLIQR model - R, (b, e, In, S, Ninv, s, Q, g, u, a, y, L) = - polynomial_ring(QQ, [:b, :e, :In, :S, :Ninv, :s, :Q, :g, :u, :a, :y, :L]) - f = [ - In // one(R), - s // one(R), - Ninv // one(R), - b // one(R), - (g + a) // one(R), - (e * s * g - s * g + g * a) // one(R), - (e * S - S) // (e * Q), - (e * S * s - S * s + S * a) // e, - (s * Q^2 - Q^2 * a) // (e * g - g), - (e * In + e * L - In - Q - L) // (e * Q), - ] - rff = StructuralIdentifiability.RationalFunctionField(f) - relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) - @test s * Q - Q * a in relations - end + ### + # Some arbitrary generators for the SLIQR model + R, (b, e, In, S, Ninv, s, Q, g, u, a, y, L) = + polynomial_ring(QQ, [:b, :e, :In, :S, :Ninv, :s, :Q, :g, :u, :a, :y, :L]) + f = [ + In // one(R), + s // one(R), + Ninv // one(R), + b // one(R), + (g + a) // one(R), + (e * s * g - s * g + g * a) // one(R), + (e * S - S) // (e * Q), + (e * S * s - S * s + S * a) // e, + (s * Q^2 - Q^2 * a) // (e * g - g), + (e * In + e * L - In - Q - L) // (e * Q), + ] + rff = StructuralIdentifiability.RationalFunctionField(f) + relations = StructuralIdentifiability.monomial_generators_up_to_degree(rff, 2) + @test s * Q - Q * a in relations end diff --git a/test/check_field_membership.jl b/test/check_field_membership.jl index 72f8eec8..8be30604 100644 --- a/test/check_field_membership.jl +++ b/test/check_field_membership.jl @@ -1,32 +1,31 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Check field membership" begin - R, (x, y, z) = Nemo.polynomial_ring(Nemo.QQ, ["x", "y", "z"]) +@testset "Check field membership" begin + R, (x, y, z) = Nemo.polynomial_ring(Nemo.QQ, ["x", "y", "z"]) - @test field_contains( - RationalFunctionField([[R(1), x + y], [R(1), x * y], [z, (x + y)^2]]), - [(x^2 + y^2) // R(1), (x^3 + y^3) // (z - x * y), R(1) // (z + x + y), z // x], - 0.99, - ) == [true, true, true, false] + @test field_contains( + RationalFunctionField([[R(1), x + y], [R(1), x * y], [z, (x + y)^2]]), + [(x^2 + y^2) // R(1), (x^3 + y^3) // (z - x * y), R(1) // (z + x + y), z // x], + 0.99, + ) == [true, true, true, false] - @test field_contains( - RationalFunctionField([[ - x + y + z, - x^2 + y^2 + z^2, - (x + y + z)^2, - x^3 + y^3 + z^3, - ]]), - [x + y + z // 1, x * y * z // 1, x + y + 2 * z // 1, x // (y + z)], - 0.99, - ) == [true, true, false, false] + @test field_contains( + RationalFunctionField([[ + x + y + z, + x^2 + y^2 + z^2, + (x + y + z)^2, + x^3 + y^3 + z^3, + ]]), + [x + y + z // 1, x * y * z // 1, x + y + 2 * z // 1, x // (y + z)], + 0.99, + ) == [true, true, false, false] - @test field_contains( - RationalFunctionField([ - x + y + z // 1, - x * y + y * z + z * x // 1, - x * y * z // 1, - ]), - [x^2 + y^2 + z^2, x^6 + y^6 + z^6, x - y + z, x^2 - y^2 + z^2], - 0.99, - ) == [true, true, false, false] - end + @test field_contains( + RationalFunctionField([ + x + y + z // 1, + x * y + y * z + z * x // 1, + x * y * z // 1, + ]), + [x^2 + y^2 + z^2, x^6 + y^6 + z^6, x - y + z, x^2 - y^2 + z^2], + 0.99, + ) == [true, true, false, false] end + diff --git a/test/differentiate_output.jl b/test/differentiate_output.jl index 4ca876c7..ff8ed54b 100644 --- a/test/differentiate_output.jl +++ b/test/differentiate_output.jl @@ -1,229 +1,227 @@ -if GROUP == "All" || GROUP == "Core" - #------- Auxiliary functions -------------------------------------------------- - - function diff_sol_Lie_derivatives(ode::ODE, params, ic, inputs, prec::Int) - # creating a new ring with variables for the derivatives of u - new_varnames = map(var_to_str, gens(ode.poly_ring)) - if length(ode.u_vars) > 0 - append!( - new_varnames, - [var_to_str(u) * "_$i" for u in ode.u_vars for i in 0:(prec - 1)], - ) - end - new_ring, vars = Nemo.polynomial_ring(base_ring(ode.poly_ring), new_varnames) +#------- Auxiliary functions -------------------------------------------------- + +function diff_sol_Lie_derivatives(ode::ODE, params, ic, inputs, prec::Int) + # creating a new ring with variables for the derivatives of u + new_varnames = map(var_to_str, gens(ode.poly_ring)) + if length(ode.u_vars) > 0 + append!( + new_varnames, + [var_to_str(u) * "_$i" for u in ode.u_vars for i in 0:(prec - 1)], + ) + end + new_ring, vars = Nemo.polynomial_ring(base_ring(ode.poly_ring), new_varnames) - # mapping everything to the new ring - eval_point = Dict(v => switch_ring(v, new_ring) for v in gens(ode.poly_ring)) - for u in ode.u_vars - eval_point[u] = str_to_var(var_to_str(u) * "_0", new_ring) - end + # mapping everything to the new ring + eval_point = Dict(v => switch_ring(v, new_ring) for v in gens(ode.poly_ring)) + for u in ode.u_vars + eval_point[u] = str_to_var(var_to_str(u) * "_0", new_ring) + end - new_eqs = Dict() - for (x, f) in ode.x_equations - new_eqs[str_to_var(var_to_str(x), new_ring)] = eval_at_dict(f, eval_point) + new_eqs = Dict() + for (x, f) in ode.x_equations + new_eqs[str_to_var(var_to_str(x), new_ring)] = eval_at_dict(f, eval_point) + end + params, ic = map( + d -> Dict(str_to_var(string(k), new_ring) => v for (k, v) in d), + [params, ic], + ) + + # computing Lie derivatives + derivation = copy(new_eqs) + for u in ode.u_vars + for i in 0:(prec - 2) + derivation[str_to_var(var_to_str(u) * "_$i", new_ring)] = + str_to_var(var_to_str(u) * "_$(i + 1)", new_ring) end - params, ic = map( - d -> Dict(str_to_var(string(k), new_ring) => v for (k, v) in d), - [params, ic], - ) + end + Lie_derivatives = Dict() + for (y, g) in ode.y_equations + Lie_derivatives[y] = Array{Any, 1}([eval_at_dict(g, eval_point)]) + for i in 1:prec + push!( + Lie_derivatives[y], + sum( + derivative(Lie_derivatives[y][end], v) * get(derivation, v, 0) for + v in gens(new_ring) + ), + ) + end + end - # computing Lie derivatives - derivation = copy(new_eqs) - for u in ode.u_vars - for i in 0:(prec - 2) - derivation[str_to_var(var_to_str(u) * "_$i", new_ring)] = - str_to_var(var_to_str(u) * "_$(i + 1)", new_ring) - end + # producing the result + eval_point = merge(params, ic) + for u in ode.u_vars + for i in 1:prec + eval_point[str_to_var(var_to_str(u) * "_$(i - 1)", new_ring)] = + inputs[u][i] * factorial(i - 1) end - Lie_derivatives = Dict() - for (y, g) in ode.y_equations - Lie_derivatives[y] = Array{Any, 1}([eval_at_dict(g, eval_point)]) - for i in 1:prec + end + + result = Dict() + for y in ode.y_vars + result[y] = Dict() + for v in vcat(ode.x_vars, ode.parameters) + result[y][v] = [] + for j in 1:prec push!( - Lie_derivatives[y], - sum( - derivative(Lie_derivatives[y][end], v) * get(derivation, v, 0) for - v in gens(new_ring) + result[y][v], + eval_at_dict( + derivative(Lie_derivatives[y][j], str_to_var("$v", new_ring)), + eval_point, ), ) end end - - # producing the result - eval_point = merge(params, ic) - for u in ode.u_vars - for i in 1:prec - eval_point[str_to_var(var_to_str(u) * "_$(i - 1)", new_ring)] = - inputs[u][i] * factorial(i - 1) - end - end - - result = Dict() - for y in ode.y_vars - result[y] = Dict() - for v in vcat(ode.x_vars, ode.parameters) - result[y][v] = [] - for j in 1:prec - push!( - result[y][v], - eval_at_dict( - derivative(Lie_derivatives[y][j], str_to_var("$v", new_ring)), - eval_point, - ), - ) - end - end - end - - return result end - #------------------------------------------------------------------------------ - - @testset "Partial derivatives of an output w.r.t. to initial conditions and parameters" begin - test_cases = [] - P = QQMPolyRingElem - DType = Union{P, Generic.Frac{P}} - - ode = @ODEmodel(x'(t) = x(t) + a, y(t) = x(t)^2) - push!( - test_cases, - Dict( - :ODE => ode, - :ic => Dict(x => Nemo.QQ(rand(1:10))), - :param_vals => Dict(a => Nemo.QQ(rand(1:10))), - :inputs => Dict{P, Array{QQFieldElem, 1}}(), - :prec => 20, - ), - ) - - ode = @ODEmodel(x'(t) = x(t)^2 + a, y1(t) = x(t) + a^2, y2(t) = x(t)^3) - push!( - test_cases, - Dict( - :ODE => ode, - :ic => Dict(x => Nemo.QQ(rand(1:10))), - :param_vals => Dict(a => Nemo.QQ(rand(1:10))), - :inputs => Dict{P, Array{QQFieldElem, 1}}(), - :prec => 20, - ), - ) - - ode = @ODEmodel( - x'(t) = x(t)^2 + 2 * x(t) * y(t) - 3 * a * y(t), - y'(t) = x(t)^2 + a * b - b^2 + 4 * b * x(t), - y1(t) = a * x(t), - y2(t) = b * y(t)^2 - y(t) - ) - push!( - test_cases, - Dict( - :ODE => ode, - :ic => Dict(x => Nemo.QQ(rand(1:10)), y => Nemo.QQ(rand(1:10))), - :param_vals => Dict(a => Nemo.QQ(rand(1:10)), b => Nemo.QQ(rand(1:10))), - :inputs => Dict{P, Array{QQFieldElem, 1}}(), - :prec => 8, - ), - ) - - ode = @ODEmodel(x'(t) = u(t) + a, y(t) = x(t)) - push!( - test_cases, - Dict( - :ODE => ode, - :ic => Dict(x => Nemo.QQ(rand(1:10))), - :param_vals => Dict(a => Nemo.QQ(rand(1:10))), - :inputs => Dict(u => [Nemo.QQ(rand(-3:3)) for i in 1:20]), - :prec => 20, - ), - ) - - F = Nemo.Native.GF(2^31 - 1) - P = fpMPolyRingElem - DType = Union{P, Generic.Frac{P}} + return result +end - varnames = vcat( - ["x_$i" for i in 1:3], - ["p_$i" for i in 1:3], - ["u_$i" for i in 1:2], - ["y_$i" for i in 1:3], - ) - R, vars = Nemo.polynomial_ring(F, varnames) - push!( - test_cases, - Dict( - :ODE => ODE{P}( - Dict{P, DType}(vars[i] => rand_poly(1, vars[1:8]) for i in 1:3), - Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 9:11), - vars[7:8], - ), - :ic => Dict(vars[i] => F(rand(1:50)) for i in 1:3), - :param_vals => Dict(vars[i + 3] => F(rand(1:50)) for i in 1:3), - :inputs => Dict(u => [F(rand(-30:30)) for i in 1:6] for u in vars[7:8]), - :prec => 6, +#------------------------------------------------------------------------------ + +@testset "Partial derivatives of an output w.r.t. to initial conditions and parameters" begin + test_cases = [] + P = QQMPolyRingElem + DType = Union{P, Generic.Frac{P}} + + ode = @ODEmodel(x'(t) = x(t) + a, y(t) = x(t)^2) + push!( + test_cases, + Dict( + :ODE => ode, + :ic => Dict(x => Nemo.QQ(rand(1:10))), + :param_vals => Dict(a => Nemo.QQ(rand(1:10))), + :inputs => Dict{P, Array{QQFieldElem, 1}}(), + :prec => 20, + ), + ) + + ode = @ODEmodel(x'(t) = x(t)^2 + a, y1(t) = x(t) + a^2, y2(t) = x(t)^3) + push!( + test_cases, + Dict( + :ODE => ode, + :ic => Dict(x => Nemo.QQ(rand(1:10))), + :param_vals => Dict(a => Nemo.QQ(rand(1:10))), + :inputs => Dict{P, Array{QQFieldElem, 1}}(), + :prec => 20, + ), + ) + + ode = @ODEmodel( + x'(t) = x(t)^2 + 2 * x(t) * y(t) - 3 * a * y(t), + y'(t) = x(t)^2 + a * b - b^2 + 4 * b * x(t), + y1(t) = a * x(t), + y2(t) = b * y(t)^2 - y(t) + ) + push!( + test_cases, + Dict( + :ODE => ode, + :ic => Dict(x => Nemo.QQ(rand(1:10)), y => Nemo.QQ(rand(1:10))), + :param_vals => Dict(a => Nemo.QQ(rand(1:10)), b => Nemo.QQ(rand(1:10))), + :inputs => Dict{P, Array{QQFieldElem, 1}}(), + :prec => 8, + ), + ) + + ode = @ODEmodel(x'(t) = u(t) + a, y(t) = x(t)) + push!( + test_cases, + Dict( + :ODE => ode, + :ic => Dict(x => Nemo.QQ(rand(1:10))), + :param_vals => Dict(a => Nemo.QQ(rand(1:10))), + :inputs => Dict(u => [Nemo.QQ(rand(-3:3)) for i in 1:20]), + :prec => 20, + ), + ) + + F = Nemo.Native.GF(2^31 - 1) + P = fpMPolyRingElem + DType = Union{P, Generic.Frac{P}} + + varnames = vcat( + ["x_$i" for i in 1:3], + ["p_$i" for i in 1:3], + ["u_$i" for i in 1:2], + ["y_$i" for i in 1:3], + ) + R, vars = Nemo.polynomial_ring(F, varnames) + push!( + test_cases, + Dict( + :ODE => ODE{P}( + Dict{P, DType}(vars[i] => rand_poly(1, vars[1:8]) for i in 1:3), + Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 9:11), + vars[7:8], ), - ) - - varnames = vcat( - ["x_$i" for i in 1:3], - ["p_$i" for i in 1:3], - ["u_$i" for i in 1:2], - ["y_$i" for i in 1:3], - ) - R, vars = Nemo.polynomial_ring(F, varnames) - push!( - test_cases, - Dict( - :ODE => ODE{P}( - Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 1:3), - Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 9:11), - vars[7:8], - ), - :ic => Dict(vars[i] => F(rand(1:50)) for i in 1:3), - :param_vals => Dict(vars[i + 3] => F(rand(1:50)) for i in 1:3), - :inputs => Dict(u => [F(rand(-30:30)) for i in 1:6] for u in vars[7:8]), - :prec => 6, + :ic => Dict(vars[i] => F(rand(1:50)) for i in 1:3), + :param_vals => Dict(vars[i + 3] => F(rand(1:50)) for i in 1:3), + :inputs => Dict(u => [F(rand(-30:30)) for i in 1:6] for u in vars[7:8]), + :prec => 6, + ), + ) + + varnames = vcat( + ["x_$i" for i in 1:3], + ["p_$i" for i in 1:3], + ["u_$i" for i in 1:2], + ["y_$i" for i in 1:3], + ) + R, vars = Nemo.polynomial_ring(F, varnames) + push!( + test_cases, + Dict( + :ODE => ODE{P}( + Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 1:3), + Dict{P, DType}(vars[i] => rand_poly(2, vars[1:8]) for i in 9:11), + vars[7:8], ), - ) - - varnames = vcat(["x_$i" for i in 1:2], ["p_$i" for i in 1:2], "u", ["y_1", "y_2"]) - R, vars = Nemo.polynomial_ring(F, varnames) - push!( - test_cases, - Dict( - :ODE => ODE{P}( - Dict{P, DType}( - vars[i] => rand_poly(1, vars[1:5]) // (vars[1] + vars[3]) for - i in 1:2 - ), - Dict{P, DType}(vars[i] => rand_poly(1, vars[1:5]) for i in 6:7), - [vars[5]], + :ic => Dict(vars[i] => F(rand(1:50)) for i in 1:3), + :param_vals => Dict(vars[i + 3] => F(rand(1:50)) for i in 1:3), + :inputs => Dict(u => [F(rand(-30:30)) for i in 1:6] for u in vars[7:8]), + :prec => 6, + ), + ) + + varnames = vcat(["x_$i" for i in 1:2], ["p_$i" for i in 1:2], "u", ["y_1", "y_2"]) + R, vars = Nemo.polynomial_ring(F, varnames) + push!( + test_cases, + Dict( + :ODE => ODE{P}( + Dict{P, DType}( + vars[i] => rand_poly(1, vars[1:5]) // (vars[1] + vars[3]) for + i in 1:2 ), - :ic => Dict(vars[i] => F(rand(1:50)) for i in 1:2), - :param_vals => Dict(vars[i + 2] => F(rand(1:50)) for i in 1:2), - :inputs => Dict(vars[5] => [F(rand(-30:30)) for i in 1:4]), - :prec => 4, + Dict{P, DType}(vars[i] => rand_poly(1, vars[1:5]) for i in 6:7), + [vars[5]], ), + :ic => Dict(vars[i] => F(rand(1:50)) for i in 1:2), + :param_vals => Dict(vars[i + 2] => F(rand(1:50)) for i in 1:2), + :inputs => Dict(vars[5] => [F(rand(-30:30)) for i in 1:4]), + :prec => 4, + ), + ) + + for case in test_cases + ode, prec = case[:ODE], case[:prec] + @time sol1 = + differentiate_output(ode, case[:param_vals], case[:ic], case[:inputs], prec) + sol2 = diff_sol_Lie_derivatives( + ode, + case[:param_vals], + case[:ic], + case[:inputs], + prec, ) - - for case in test_cases - ode, prec = case[:ODE], case[:prec] - @time sol1 = - differentiate_output(ode, case[:param_vals], case[:ic], case[:inputs], prec) - sol2 = diff_sol_Lie_derivatives( - ode, - case[:param_vals], - case[:ic], - case[:inputs], - prec, - ) - for y in ode.y_vars - for v in vcat(ode.x_vars, ode.parameters) - @test sol2[y][v] == [ - base_ring(ode.poly_ring)(coeff(sol1[y][v], j) * factorial(j)) for - j in 0:(prec - 1) - ] - end + for y in ode.y_vars + for v in vcat(ode.x_vars, ode.parameters) + @test sol2[y][v] == [ + base_ring(ode.poly_ring)(coeff(sol1[y][v], j) * factorial(j)) for + j in 0:(prec - 1) + ] end end end diff --git a/test/diffreduction.jl b/test/diffreduction.jl index 556a44d5..82150ac2 100644 --- a/test/diffreduction.jl +++ b/test/diffreduction.jl @@ -1,166 +1,164 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Differential reduction" begin - ode = @ODEmodel(x'(t) = a * x(t), y(t) = x(t)) - pbr = PBRepresentation(ode, find_ioequations(ode)) - R, (y_5, y_4) = Nemo.polynomial_ring(Nemo.QQ, ["y(t)_5", "y(t)_4"]) - res = diffreduce(y_5, pbr) - @test res == str_to_var("a", parent(res))^5 * str_to_var("y(t)_0", parent(res)) - res = diffreduce(y_4, pbr) - @test res == str_to_var("a", parent(res))^4 * str_to_var("y(t)_0", parent(res)) - res = diffreduce(y_4^2, pbr) - @test res == str_to_var("a", parent(res))^8 * str_to_var("y(t)_0", parent(res))^2 +@testset "Differential reduction" begin + ode = @ODEmodel(x'(t) = a * x(t), y(t) = x(t)) + pbr = PBRepresentation(ode, find_ioequations(ode)) + R, (y_5, y_4) = Nemo.polynomial_ring(Nemo.QQ, ["y(t)_5", "y(t)_4"]) + res = diffreduce(y_5, pbr) + @test res == str_to_var("a", parent(res))^5 * str_to_var("y(t)_0", parent(res)) + res = diffreduce(y_4, pbr) + @test res == str_to_var("a", parent(res))^4 * str_to_var("y(t)_0", parent(res)) + res = diffreduce(y_4^2, pbr) + @test res == str_to_var("a", parent(res))^8 * str_to_var("y(t)_0", parent(res))^2 - ode = @ODEmodel(x1'(t) = x2(t), x2'(t) = -x1(t), y(t) = x1(t) + u(t)) - pbr = PBRepresentation(ode, find_ioequations(ode)) - R, (y_5, y_4, u_10) = Nemo.polynomial_ring(Nemo.QQ, ["y(t)_5", "y(t)_4", "u(t)_10"]) - res = diffreduce(y_4 + u_10, pbr) - @test res == - str_to_var("u(t)_10", parent(res)) + str_to_var("y(t)_0", parent(res)) - - str_to_var("u(t)_0", parent(res)) + str_to_var("u(t)_4", parent(res)) + ode = @ODEmodel(x1'(t) = x2(t), x2'(t) = -x1(t), y(t) = x1(t) + u(t)) + pbr = PBRepresentation(ode, find_ioequations(ode)) + R, (y_5, y_4, u_10) = Nemo.polynomial_ring(Nemo.QQ, ["y(t)_5", "y(t)_4", "u(t)_10"]) + res = diffreduce(y_4 + u_10, pbr) + @test res == + str_to_var("u(t)_10", parent(res)) + str_to_var("y(t)_0", parent(res)) - + str_to_var("u(t)_0", parent(res)) + str_to_var("u(t)_4", parent(res)) - # Next two verified with Maple - # Mizuka's example - R, (y_0, y_1, y_2, y_3, u_0, u_1, u_2) = - Nemo.polynomial_ring(Nemo.QQ, ["y_0", "y_1", "y_2", "y_3", "u_0", "u_1", "u_2"]) - pbr = PBRepresentation( - ["y"], - ["u"], - Array{String, 1}(), - Dict("y" => 2), - Dict( - "y" => - 27 * y_0^9 + 27 * y_0^6 * y_1^3 - 54 * u_0 * y_0^6 + - 54 * y_0^7 + - 54 * y_0^6 * y_1 - 27 * y_0^5 * y_1^2 - 27 * y_0^4 * u_1 * y_1^2 + - 27 * y_0^4 * y_1^3 + - 27 * y_0^4 * y_1^2 * y_2 + - 27 * u_0^2 * y_0^3 - 54 * u_0 * y_0^4 - 54 * u_0 * y_0^3 * y_1 + - 27 * y_0^5 + - 54 * y_0^4 * y_1 + - 18 * y_0^3 * u_1 * y_1 + - 9 * y_0^3 * y_1^2 - 18 * y_0^3 * y_1 * y_2 + - 9 * y_0^2 * u_1^2 * y_1 - 18 * y_0^2 * u_1 * y_1^2 - - 18 * y_0^2 * u_1 * y_1 * y_2 + - 9 * y_0^2 * y_1^3 + - 18 * y_0^2 * y_1^2 * y_2 + - 9 * y_0^2 * y_1 * y_2^2 + - 4 * y_0^3 - 3 * y_0 * u_1^2 + - 6 * y_0 * u_1 * y_1 + - 6 * y_0 * u_1 * y_2 - 3 * y_0 * y_1^2 - 6 * y_0 * y_1 * y_2 - - 3 * y_0 * y_2^2 - u_1^3 + - 3 * u_1^2 * y_1 + - 3 * u_1^2 * y_2 - 3 * u_1 * y_1^2 - 6 * u_1 * y_1 * y_2 - - 3 * u_1 * y_2^2 + - y_1^3 + - 3 * y_1^2 * y_2 + - 3 * y_1 * y_2^2 + - y_2^3, - ), - ) - p = - 100 * y_0^4 + 10 * y_0^3 * y_1 + 410 // 3 * y_0 * y_2 + 10 * y_0 * y_3 - - 110 * y_1^2 - 10 * y_1 * y_2 - @time res = diffreduce(p, pbr) - expected = parent_ring_change( - -100 * y_2^2 * y_1^2 + 70 * y_2^2 * y_0^4 + 380 // 3 * y_2^2 * y_0^2 - - 200 * y_2 * y_1^3 - 180 * y_2 * y_0^7 - 380 * y_2 * y_0^5 - - 270 * y_1^4 * y_0^6 - 1080 * y_1^4 * y_0^4 - 630 * y_1^4 * y_0^2 + - 810 * y_1^3 * y_0^9 - 2520 * y_1^3 * y_0^7 - 2910 * y_1^3 * y_0^5 - - 440 * y_1^3 * y_0^3 + - 220 // 3 * y_1^3 * y_0 + - 90 * y_1^2 * y_0^8 + - 3690 * y_1^2 * y_0^6 - 1330 * y_1^2 * y_0^4 + - 380 * y_1^2 * y_0^2 + - 1080 * y_1 * y_0^9 - 6540 * y_1 * y_0^7 - 7220 * y_1 * y_0^5 + - 810 * u_0^2 * y_0^6 - 3420 * u_0^2 * y_0^4 - 1620 * u_0 * y_0^9 + - 5220 * u_0 * y_0^7 + - 6840 * u_0 * y_0^5 - 10 * u_1^3 * y_1 - 30 * u_1^3 * y_0^3 + - 380 // 3 * u_1^3 * y_0 - 80 * u_1^2 * y_1^2 + - 10 * u_1^2 * y_0^4 + - 380 * u_1^2 * y_0^2 + - 190 * u_1 * y_1^3 + - 180 * u_1 * y_0^7 + - 380 * u_1 * y_0^5 - 3300 * y_0^6 - 1520 // 3 * y_0^4 - - 20 * u_2 * y_2 * u_1 * y_0 + - 60 * u_2 * y_2 * y_1 * y_0^3 + - 20 * u_2 * y_2 * y_1 * y_0 - 60 * u_2 * u_1 * y_1 * y_0^3 - - 20 * u_2 * u_1 * y_1 * y_0 - 360 * y_2 * u_1 * y_1 * y_0^5 + - 1380 * y_2 * u_1 * y_1 * y_0^3 + - 1580 // 3 * y_2 * u_1 * y_1 * y_0 - 30 * y_2^2 * y_1^2 * y_0^2 + - 90 * y_2^2 * y_1 * y_0^5 - 340 * y_2^2 * y_1 * y_0^3 - - 380 // 3 * y_2^2 * y_1 * y_0 - 180 * y_2 * y_1^3 * y_0^4 - - 660 * y_2 * y_1^3 * y_0^2 + 540 * y_2 * y_1^2 * y_0^7 - - 1860 * y_2 * y_1^2 * y_0^5 - 1380 * y_2 * y_1^2 * y_0^3 - - 160 // 3 * y_2 * y_1^2 * y_0 + - 240 * y_2 * y_1 * y_0^6 + - 1400 * y_2 * y_1 * y_0^4 + - 1520 // 3 * y_2 * y_1 * y_0^2 + - 10 * u_2 * y_2^2 * y_0 - 20 * u_2 * y_2 * y_0^2 + - 10 * u_2 * u_1^2 * y_0 + - 20 * u_2 * u_1 * y_0^2 + - 90 * u_2 * y_1^2 * y_0^5 + - 60 * u_2 * y_1^2 * y_0^3 + - 10 * u_2 * y_1^2 * y_0 - 60 * u_2 * y_1 * y_0^4 - 20 * u_2 * y_1 * y_0^2 - - 10 * y_2^2 * u_1 * y_1 - 30 * y_2^2 * u_1 * y_0^3 + - 380 // 3 * y_2^2 * u_1 * y_0 + - 20 * y_2 * u_1^2 * y_1 + - 60 * y_2 * u_1^2 * y_0^3 - 760 // 3 * y_2 * u_1^2 * y_0 + - 180 * y_2 * u_1 * y_1^2 - 80 * y_2 * u_1 * y_0^4 - - 1520 // 3 * y_2 * u_1 * y_0^2 + - 180 * y_2 * u_0 * y_0^4 + - 30 * u_1^2 * y_1^2 * y_0^2 + - 270 * u_1^2 * y_1 * y_0^5 - 1040 * u_1^2 * y_1 * y_0^3 - - 400 * u_1^2 * y_1 * y_0 + - 90 * u_1 * y_1^3 * y_0^4 + - 600 * u_1 * y_1^3 * y_0^2 - 810 * u_1 * y_1^2 * y_0^7 + - 2820 * u_1 * y_1^2 * y_0^5 + - 2170 * u_1 * y_1^2 * y_0^3 + - 200 * u_1 * y_1^2 * y_0 - 60 * u_1 * y_1 * y_0^6 - - 2100 * u_1 * y_1 * y_0^4 - 760 * u_1 * y_1 * y_0^2 - - 180 * u_1 * u_0 * y_0^4 - 1080 * y_1 * u_0 * y_0^6 + - 7020 * y_1 * u_0 * y_0^4 - 6030 * y_0^8 + 810 * y_0^12 - 1800 * y_0^10 - - 100 * y_1^4, - parent(res), - ) - @test total_degree(divexact(res, expected)) == 0 + # Next two verified with Maple + # Mizuka's example + R, (y_0, y_1, y_2, y_3, u_0, u_1, u_2) = + Nemo.polynomial_ring(Nemo.QQ, ["y_0", "y_1", "y_2", "y_3", "u_0", "u_1", "u_2"]) + pbr = PBRepresentation( + ["y"], + ["u"], + Array{String, 1}(), + Dict("y" => 2), + Dict( + "y" => + 27 * y_0^9 + 27 * y_0^6 * y_1^3 - 54 * u_0 * y_0^6 + + 54 * y_0^7 + + 54 * y_0^6 * y_1 - 27 * y_0^5 * y_1^2 - 27 * y_0^4 * u_1 * y_1^2 + + 27 * y_0^4 * y_1^3 + + 27 * y_0^4 * y_1^2 * y_2 + + 27 * u_0^2 * y_0^3 - 54 * u_0 * y_0^4 - 54 * u_0 * y_0^3 * y_1 + + 27 * y_0^5 + + 54 * y_0^4 * y_1 + + 18 * y_0^3 * u_1 * y_1 + + 9 * y_0^3 * y_1^2 - 18 * y_0^3 * y_1 * y_2 + + 9 * y_0^2 * u_1^2 * y_1 - 18 * y_0^2 * u_1 * y_1^2 - + 18 * y_0^2 * u_1 * y_1 * y_2 + + 9 * y_0^2 * y_1^3 + + 18 * y_0^2 * y_1^2 * y_2 + + 9 * y_0^2 * y_1 * y_2^2 + + 4 * y_0^3 - 3 * y_0 * u_1^2 + + 6 * y_0 * u_1 * y_1 + + 6 * y_0 * u_1 * y_2 - 3 * y_0 * y_1^2 - 6 * y_0 * y_1 * y_2 - + 3 * y_0 * y_2^2 - u_1^3 + + 3 * u_1^2 * y_1 + + 3 * u_1^2 * y_2 - 3 * u_1 * y_1^2 - 6 * u_1 * y_1 * y_2 - + 3 * u_1 * y_2^2 + + y_1^3 + + 3 * y_1^2 * y_2 + + 3 * y_1 * y_2^2 + + y_2^3, + ), + ) + p = + 100 * y_0^4 + 10 * y_0^3 * y_1 + 410 // 3 * y_0 * y_2 + 10 * y_0 * y_3 - + 110 * y_1^2 - 10 * y_1 * y_2 + @time res = diffreduce(p, pbr) + expected = parent_ring_change( + -100 * y_2^2 * y_1^2 + 70 * y_2^2 * y_0^4 + 380 // 3 * y_2^2 * y_0^2 - + 200 * y_2 * y_1^3 - 180 * y_2 * y_0^7 - 380 * y_2 * y_0^5 - + 270 * y_1^4 * y_0^6 - 1080 * y_1^4 * y_0^4 - 630 * y_1^4 * y_0^2 + + 810 * y_1^3 * y_0^9 - 2520 * y_1^3 * y_0^7 - 2910 * y_1^3 * y_0^5 - + 440 * y_1^3 * y_0^3 + + 220 // 3 * y_1^3 * y_0 + + 90 * y_1^2 * y_0^8 + + 3690 * y_1^2 * y_0^6 - 1330 * y_1^2 * y_0^4 + + 380 * y_1^2 * y_0^2 + + 1080 * y_1 * y_0^9 - 6540 * y_1 * y_0^7 - 7220 * y_1 * y_0^5 + + 810 * u_0^2 * y_0^6 - 3420 * u_0^2 * y_0^4 - 1620 * u_0 * y_0^9 + + 5220 * u_0 * y_0^7 + + 6840 * u_0 * y_0^5 - 10 * u_1^3 * y_1 - 30 * u_1^3 * y_0^3 + + 380 // 3 * u_1^3 * y_0 - 80 * u_1^2 * y_1^2 + + 10 * u_1^2 * y_0^4 + + 380 * u_1^2 * y_0^2 + + 190 * u_1 * y_1^3 + + 180 * u_1 * y_0^7 + + 380 * u_1 * y_0^5 - 3300 * y_0^6 - 1520 // 3 * y_0^4 - + 20 * u_2 * y_2 * u_1 * y_0 + + 60 * u_2 * y_2 * y_1 * y_0^3 + + 20 * u_2 * y_2 * y_1 * y_0 - 60 * u_2 * u_1 * y_1 * y_0^3 - + 20 * u_2 * u_1 * y_1 * y_0 - 360 * y_2 * u_1 * y_1 * y_0^5 + + 1380 * y_2 * u_1 * y_1 * y_0^3 + + 1580 // 3 * y_2 * u_1 * y_1 * y_0 - 30 * y_2^2 * y_1^2 * y_0^2 + + 90 * y_2^2 * y_1 * y_0^5 - 340 * y_2^2 * y_1 * y_0^3 - + 380 // 3 * y_2^2 * y_1 * y_0 - 180 * y_2 * y_1^3 * y_0^4 - + 660 * y_2 * y_1^3 * y_0^2 + 540 * y_2 * y_1^2 * y_0^7 - + 1860 * y_2 * y_1^2 * y_0^5 - 1380 * y_2 * y_1^2 * y_0^3 - + 160 // 3 * y_2 * y_1^2 * y_0 + + 240 * y_2 * y_1 * y_0^6 + + 1400 * y_2 * y_1 * y_0^4 + + 1520 // 3 * y_2 * y_1 * y_0^2 + + 10 * u_2 * y_2^2 * y_0 - 20 * u_2 * y_2 * y_0^2 + + 10 * u_2 * u_1^2 * y_0 + + 20 * u_2 * u_1 * y_0^2 + + 90 * u_2 * y_1^2 * y_0^5 + + 60 * u_2 * y_1^2 * y_0^3 + + 10 * u_2 * y_1^2 * y_0 - 60 * u_2 * y_1 * y_0^4 - 20 * u_2 * y_1 * y_0^2 - + 10 * y_2^2 * u_1 * y_1 - 30 * y_2^2 * u_1 * y_0^3 + + 380 // 3 * y_2^2 * u_1 * y_0 + + 20 * y_2 * u_1^2 * y_1 + + 60 * y_2 * u_1^2 * y_0^3 - 760 // 3 * y_2 * u_1^2 * y_0 + + 180 * y_2 * u_1 * y_1^2 - 80 * y_2 * u_1 * y_0^4 - + 1520 // 3 * y_2 * u_1 * y_0^2 + + 180 * y_2 * u_0 * y_0^4 + + 30 * u_1^2 * y_1^2 * y_0^2 + + 270 * u_1^2 * y_1 * y_0^5 - 1040 * u_1^2 * y_1 * y_0^3 - + 400 * u_1^2 * y_1 * y_0 + + 90 * u_1 * y_1^3 * y_0^4 + + 600 * u_1 * y_1^3 * y_0^2 - 810 * u_1 * y_1^2 * y_0^7 + + 2820 * u_1 * y_1^2 * y_0^5 + + 2170 * u_1 * y_1^2 * y_0^3 + + 200 * u_1 * y_1^2 * y_0 - 60 * u_1 * y_1 * y_0^6 - + 2100 * u_1 * y_1 * y_0^4 - 760 * u_1 * y_1 * y_0^2 - + 180 * u_1 * u_0 * y_0^4 - 1080 * y_1 * u_0 * y_0^6 + + 7020 * y_1 * u_0 * y_0^4 - 6030 * y_0^8 + 810 * y_0^12 - 1800 * y_0^10 - + 100 * y_1^4, + parent(res), + ) + @test total_degree(divexact(res, expected)) == 0 - ode = @ODEmodel( - x1'(t) = x1(t) + 2 * x1(t) * x2(t) + u(t), - x2'(t) = x2(t) + 3 * x1(t) * x2(t), - y(t) = x1(t) - ) - pbr = PBRepresentation(ode, find_ioequations(ode)) - R, (y_0, y_1, y_2, y_3, y_4, u_0, u_3) = Nemo.polynomial_ring( - Nemo.QQ, - ["y(t)_0", "y(t)_1", "y(t)_2", "y(t)_3", "y(t)_4", "u(t)_0", "u(t)_3"], - ) - io_switch!(pbr) - @time res = diffreduce(u_3, pbr) - expected = parent_ring_change( - y_0 * ( - 27 * u_0 * y_0^4 + 27 * y_0^5 - 27 * y_0^4 * y_1 + - 27 * u_0 * y_0^3 + - 54 * u_0 * y_0^2 * y_1 + - 27 * y_0^4 + - 27 * y_0^3 * y_1 - 54 * y_0^2 * y_1^2 + - 9 * u_0 * y_0^2 + - 27 * u_0 * y_0 * y_1 + - 12 * u_0 * y_0 * y_2 + - 9 * u_0 * y_1^2 + - 9 * y_0^3 + - 18 * y_0^2 * y_1 + - 12 * y_0^2 * y_2 - 18 * y_0 * y_1^2 - 12 * y_0 * y_1 * y_2 - 9 * y_1^3 + - u_0 * y_0 + - 3 * u_0 * y_1 + - 3 * u_0 * y_2 + - u_0 * y_3 + - y_0^2 + - 2 * y_0 * y_1 + - 3 * y_0 * y_2 + - y_0 * y_4 - 3 * y_1^2 - 3 * y_1 * y_2 - y_1 * y_3 - ), - parent(res), - ) - @test total_degree(divexact(res, expected)) == 0 - end + ode = @ODEmodel( + x1'(t) = x1(t) + 2 * x1(t) * x2(t) + u(t), + x2'(t) = x2(t) + 3 * x1(t) * x2(t), + y(t) = x1(t) + ) + pbr = PBRepresentation(ode, find_ioequations(ode)) + R, (y_0, y_1, y_2, y_3, y_4, u_0, u_3) = Nemo.polynomial_ring( + Nemo.QQ, + ["y(t)_0", "y(t)_1", "y(t)_2", "y(t)_3", "y(t)_4", "u(t)_0", "u(t)_3"], + ) + io_switch!(pbr) + @time res = diffreduce(u_3, pbr) + expected = parent_ring_change( + y_0 * ( + 27 * u_0 * y_0^4 + 27 * y_0^5 - 27 * y_0^4 * y_1 + + 27 * u_0 * y_0^3 + + 54 * u_0 * y_0^2 * y_1 + + 27 * y_0^4 + + 27 * y_0^3 * y_1 - 54 * y_0^2 * y_1^2 + + 9 * u_0 * y_0^2 + + 27 * u_0 * y_0 * y_1 + + 12 * u_0 * y_0 * y_2 + + 9 * u_0 * y_1^2 + + 9 * y_0^3 + + 18 * y_0^2 * y_1 + + 12 * y_0^2 * y_2 - 18 * y_0 * y_1^2 - 12 * y_0 * y_1 * y_2 - 9 * y_1^3 + + u_0 * y_0 + + 3 * u_0 * y_1 + + 3 * u_0 * y_2 + + u_0 * y_3 + + y_0^2 + + 2 * y_0 * y_1 + + 3 * y_0 * y_2 + + y_0 * y_4 - 3 * y_1^2 - 3 * y_1 * y_2 - y_1 * y_3 + ), + parent(res), + ) + @test total_degree(divexact(res, expected)) == 0 end diff --git a/test/exp_vec_trie.jl b/test/exp_vec_trie.jl index ec09c542..11420ecd 100644 --- a/test/exp_vec_trie.jl +++ b/test/exp_vec_trie.jl @@ -1,32 +1,30 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Trie for exponents vectors" begin - for _ in 1:10 - d = rand([i + 10 for i in 1:10]) - t = ExpVectTrie(d) - push!(t, [0 for _ in 1:d]) - vects = [rand([i for i in 1:10], d) for _ in 1:20] - for v in vects - push!(t, v) - end - for v in vects - diff, best = get_max_below(t, v) - @test diff == 0 - @test best == v - end +@testset "Trie for exponents vectors" begin + for _ in 1:10 + d = rand([i + 10 for i in 1:10]) + t = ExpVectTrie(d) + push!(t, [0 for _ in 1:d]) + vects = [rand([i for i in 1:10], d) for _ in 1:20] + for v in vects + push!(t, v) + end + for v in vects + diff, best = get_max_below(t, v) + @test diff == 0 + @test best == v + end - svects = Set(vects) - push!(svects, [0 for _ in 1:d]) + svects = Set(vects) + push!(svects, [0 for _ in 1:d]) - for _ in 1:20 - v = rand([i for i in 1:10], d) - diff, best = get_max_below(t, v) - if v in svects - @test diff == 0 - @test v == best - else - @test (best in svects) - @test diff == sum(v .- best) - end + for _ in 1:20 + v = rand([i for i in 1:10], d) + diff, best = get_max_below(t, v) + if v in svects + @test diff == 0 + @test v == best + else + @test (best in svects) + @test diff == sum(v .- best) end end end diff --git a/test/extensions/modelingtoolkit.jl b/test/extensions/modelingtoolkit.jl index 0a37b8f4..a917066d 100644 --- a/test/extensions/modelingtoolkit.jl +++ b/test/extensions/modelingtoolkit.jl @@ -1,688 +1,686 @@ -@static if VERSION >= v"1.10.0" - if GROUP == "All" || GROUP == "ModelingToolkitExt" - @testset "Check identifiability of `ODESystem` object" begin - using ModelingToolkit - using ModelingToolkit: parameters - using Symbolics - - @parameters a01 a21 a12 - @variables t x0(t) x1(t) y1(t) [output = true] - D = Differential(t) - - eqs = - [D(x0) ~ -(a01 + a21) * x0 + a12 * x1, D(x1) ~ a21 * x0 - a12 * x1, y1 ~ x0] - de = ODESystem(eqs, t, name = :Test) - - correct = OrderedDict( - a01 => :nonidentifiable, - a21 => :nonidentifiable, - a12 => :nonidentifiable, - x0 => :globally, - x1 => :nonidentifiable, - ) - - @test isequal( - correct, - assess_identifiability(de; measured_quantities = [y1 ~ x0]), - ) - @test isequal(correct, assess_identifiability(de; measured_quantities = [x0])) - @test isequal( - correct, - assess_identifiability(de; measured_quantities = [(y1 ~ x0).rhs]), - ) - - # check identifiabile functions - correct = [a01 * a12, a01 + a12 + a21] - result = find_identifiable_functions(de, measured_quantities = [y1 ~ x0]) - @test isequal(Set(correct), Set(result)) - - # -------------------------------------------------------------------------- - - # check identifiabile functions - @parameters V_m k_m k01 c - @variables t x(t) y1(t) [output = true] - D = Differential(t) - - eqs = [D(x) ~ (-V_m * x) / (k_m + x) + k01 * x, y1 ~ c * x] - de = ODESystem(eqs, t, name = :Test) - - correct = [k01, c * k_m, V_m * c] - result = find_identifiable_functions(de) - @test isequal(Set(correct), Set(result)) - - correct = [k01, c * x, k_m * c, V_m * c] - result = find_identifiable_functions(de, with_states = true) - @test isequal(Set(correct), Set(result)) - - # -------------------------------------------------------------------------- - @parameters a01 a21 a12 - @variables t x0(t) x1(t) y1(t) [output = true] - D = Differential(t) - - eqs = - [D(x0) ~ -(a01 + a21) * x0 + a12 * x1, D(x1) ~ a21 * x0 - a12 * x1, y1 ~ x0] - de = ODESystem(eqs, t, name = :Test) - - correct = OrderedDict( - a01 => :nonidentifiable, - a21 => :nonidentifiable, - a12 => :nonidentifiable, - x0 => :globally, - x1 => :nonidentifiable, - ) - - @test isequal(correct, assess_identifiability(de)) - - # -------------------------------------------------------------------------- - - @parameters a01 a21 a12 - @variables t x0(t) x1(t) y1(t) [output = true] - D = Differential(t) - - eqs = - [D(x0) ~ -(a01 + a21) * x0 + a12 * x1, D(x1) ~ a21 * x0 - a12 * x1, y1 ~ x0] - de = ODESystem(eqs, t, name = :Test) - funcs_to_check = [a01, a21, a12, a01 * a12, a01 + a12 + a21] - correct = OrderedDict( - a01 => :nonidentifiable, - a21 => :nonidentifiable, - a12 => :nonidentifiable, - a01 * a12 => :globally, - a01 + a12 + a21 => :globally, - ) - @test isequal( - correct, - assess_identifiability(de; funcs_to_check = funcs_to_check), - ) - - # -------------------------------------------------------------------------- - - @parameters a01 a21 a12 - @variables t x0(t) x1(t) y1(t) - D = Differential(t) - - eqs = [D(x0) ~ -(a01 + a21) * x0 + a12 * x1, D(x1) ~ a21 * x0 - a12 * x1] - measured_quantities = [y1 ~ x0] - de = ODESystem(eqs, t, name = :Test) - funcs_to_check = [a01, a21, a12, a01 * a12, a01 + a12 + a21] - correct = OrderedDict( - a01 => :nonidentifiable, - a21 => :nonidentifiable, - a12 => :nonidentifiable, - a01 * a12 => :globally, - a01 + a12 + a21 => :globally, - ) - @test isequal( - correct, - assess_identifiability( - de; - measured_quantities = measured_quantities, - funcs_to_check = funcs_to_check, - ), - ) - - # -------------------------------------------------------------------------- - @parameters μ bi bw a χ γ k - @variables t S(t) I(t) W(t) R(t) y(t) - - eqs = [ - D(S) ~ μ - bi * S * I - bw * S * W - μ * S + a * R, - D(I) ~ bw * S * W + bi * S * I - (γ + μ) * I, - D(W) ~ χ * (I - W), - D(R) ~ γ * I - (μ + a) * R, - ] - de = ODESystem(eqs, t, name = :TestSIWR) - measured_quantities = [y ~ k * I] - # check all parameters (default) - @test isequal( - true, - all( - values( - assess_local_identifiability( - de; - measured_quantities = measured_quantities, - ), - ), - ), - ) - - # check specific parameters - funcs_to_check = [μ, bi, bw, a, χ, γ, γ + μ, k, S, I, W, R] - correct = OrderedDict(f => true for f in funcs_to_check) - @test isequal( - correct, - assess_local_identifiability( - de; - measured_quantities = measured_quantities, - funcs_to_check = funcs_to_check, - ), - ) - - # checking ME identifiability - funcs_to_check = [μ, bi, bw, a, χ, γ, γ + μ, k] - correct = OrderedDict(f => true for f in funcs_to_check) - @test isequal( - (correct, 1), - assess_local_identifiability( - de; - measured_quantities = measured_quantities, - funcs_to_check = funcs_to_check, - prob_threshold = 0.99, - type = :ME, - ), - ) - - # checking identifiabile functions - correct = [a, bw, χ, bi, k, γ, μ] - result = - find_identifiable_functions(de, measured_quantities = measured_quantities) - @test isequal(Set(correct), Set(result)) - - # -------------------------------------------------------------------------- - @parameters mu bi bw a xi gm k - @variables t S(t) I(t) W(t) R(t) y(t) [output = true] - - eqs = [ - D(S) ~ mu - bi * S * I - bw * S * W - mu * S + a * R, - D(I) ~ bw * S * W + bi * S * I - (gm + mu) * I, - D(W) ~ xi * (I - W), - D(R) ~ gm * I - (mu + a) * R, - y ~ k * I, - ] - de = ODESystem(eqs, t, name = :TestSIWR) - # check all parameters (default) - @test isequal(true, all(values(assess_local_identifiability(de)))) - - @test isequal( - true, - all( - values( - assess_local_identifiability(de; measured_quantities = [y ~ k * I]), +if GROUP == "All" || GROUP == "ModelingToolkitExt" + @testset "Check identifiability of `ODESystem` object" begin + using ModelingToolkit + using ModelingToolkit: parameters + using Symbolics + + @parameters a01 a21 a12 + @variables t x0(t) x1(t) y1(t) [output = true] + D = Differential(t) + + eqs = + [D(x0) ~ -(a01 + a21) * x0 + a12 * x1, D(x1) ~ a21 * x0 - a12 * x1, y1 ~ x0] + de = ODESystem(eqs, t, name = :Test) + + correct = OrderedDict( + a01 => :nonidentifiable, + a21 => :nonidentifiable, + a12 => :nonidentifiable, + x0 => :globally, + x1 => :nonidentifiable, + ) + + @test isequal( + correct, + assess_identifiability(de; measured_quantities = [y1 ~ x0]), + ) + @test isequal(correct, assess_identifiability(de; measured_quantities = [x0])) + @test isequal( + correct, + assess_identifiability(de; measured_quantities = [(y1 ~ x0).rhs]), + ) + + # check identifiabile functions + correct = [a01 * a12, a01 + a12 + a21] + result = find_identifiable_functions(de, measured_quantities = [y1 ~ x0]) + @test isequal(Set(correct), Set(result)) + + # -------------------------------------------------------------------------- + + # check identifiabile functions + @parameters V_m k_m k01 c + @variables t x(t) y1(t) [output = true] + D = Differential(t) + + eqs = [D(x) ~ (-V_m * x) / (k_m + x) + k01 * x, y1 ~ c * x] + de = ODESystem(eqs, t, name = :Test) + + correct = [k01, c * k_m, V_m * c] + result = find_identifiable_functions(de) + @test isequal(Set(correct), Set(result)) + + correct = [k01, c * x, k_m * c, V_m * c] + result = find_identifiable_functions(de, with_states = true) + @test isequal(Set(correct), Set(result)) + + # -------------------------------------------------------------------------- + @parameters a01 a21 a12 + @variables t x0(t) x1(t) y1(t) [output = true] + D = Differential(t) + + eqs = + [D(x0) ~ -(a01 + a21) * x0 + a12 * x1, D(x1) ~ a21 * x0 - a12 * x1, y1 ~ x0] + de = ODESystem(eqs, t, name = :Test) + + correct = OrderedDict( + a01 => :nonidentifiable, + a21 => :nonidentifiable, + a12 => :nonidentifiable, + x0 => :globally, + x1 => :nonidentifiable, + ) + + @test isequal(correct, assess_identifiability(de)) + + # -------------------------------------------------------------------------- + + @parameters a01 a21 a12 + @variables t x0(t) x1(t) y1(t) [output = true] + D = Differential(t) + + eqs = + [D(x0) ~ -(a01 + a21) * x0 + a12 * x1, D(x1) ~ a21 * x0 - a12 * x1, y1 ~ x0] + de = ODESystem(eqs, t, name = :Test) + funcs_to_check = [a01, a21, a12, a01 * a12, a01 + a12 + a21] + correct = OrderedDict( + a01 => :nonidentifiable, + a21 => :nonidentifiable, + a12 => :nonidentifiable, + a01 * a12 => :globally, + a01 + a12 + a21 => :globally, + ) + @test isequal( + correct, + assess_identifiability(de; funcs_to_check = funcs_to_check), + ) + + # -------------------------------------------------------------------------- + + @parameters a01 a21 a12 + @variables t x0(t) x1(t) y1(t) + D = Differential(t) + + eqs = [D(x0) ~ -(a01 + a21) * x0 + a12 * x1, D(x1) ~ a21 * x0 - a12 * x1] + measured_quantities = [y1 ~ x0] + de = ODESystem(eqs, t, name = :Test) + funcs_to_check = [a01, a21, a12, a01 * a12, a01 + a12 + a21] + correct = OrderedDict( + a01 => :nonidentifiable, + a21 => :nonidentifiable, + a12 => :nonidentifiable, + a01 * a12 => :globally, + a01 + a12 + a21 => :globally, + ) + @test isequal( + correct, + assess_identifiability( + de; + measured_quantities = measured_quantities, + funcs_to_check = funcs_to_check, + ), + ) + + # -------------------------------------------------------------------------- + @parameters μ bi bw a χ γ k + @variables t S(t) I(t) W(t) R(t) y(t) + + eqs = [ + D(S) ~ μ - bi * S * I - bw * S * W - μ * S + a * R, + D(I) ~ bw * S * W + bi * S * I - (γ + μ) * I, + D(W) ~ χ * (I - W), + D(R) ~ γ * I - (μ + a) * R, + ] + de = ODESystem(eqs, t, name = :TestSIWR) + measured_quantities = [y ~ k * I] + # check all parameters (default) + @test isequal( + true, + all( + values( + assess_local_identifiability( + de; + measured_quantities = measured_quantities, ), ), - ) - - # check specific parameters - funcs_to_check = [mu, bi, bw, a, xi, gm, gm + mu, k, S, I, W, R] - correct = OrderedDict(f => true for f in funcs_to_check) - @test isequal( - correct, - assess_local_identifiability(de; funcs_to_check = funcs_to_check), - ) - - # checking ME identifiability - funcs_to_check = [mu, bi, bw, a, xi, gm, gm + mu, k] - correct = OrderedDict(f => true for f in funcs_to_check) - @test isequal( - (correct, 1), - assess_local_identifiability( - de; - funcs_to_check = funcs_to_check, - prob_threshold = 0.99, - type = :ME, - ), - ) - - # -------------------------------------------------------------------------- - @parameters mu bi bw a xi gm k - @variables t S(t) I(t) W(t) R(t) y(t) - - eqs = [ - D(S) ~ 2.0 * mu - bi * S * I - bw * S * W - mu * S + a * R, - D(I) ~ bw * S * W + bi * S * I - (gm + mu) * I, - D(W) ~ xi * (I - 0.6 * W), - D(R) ~ gm * I - (mu + a) * R, - ] - de = ODESystem(eqs, t, name = :TestSIWR) - measured_quantities = [y ~ 1.57 * I * k] - funcs_to_check = [mu, bi, bw, a, xi, gm, mu, gm + mu, k, S, I, W, R] - correct = OrderedDict(f => true for f in funcs_to_check) - @test isequal( - correct, - assess_local_identifiability( - de; - measured_quantities = measured_quantities, - funcs_to_check = funcs_to_check, - ), - ) - - # checking ME identifiability - funcs_to_check = [bi, bw, a, xi, gm, mu, gm + mu, k] - correct = OrderedDict(f => true for f in funcs_to_check) - @test isequal( - (correct, 1), - assess_local_identifiability( - de; - measured_quantities = measured_quantities, - funcs_to_check = funcs_to_check, - prob_threshold = 0.99, - type = :ME, + ), + ) + + # check specific parameters + funcs_to_check = [μ, bi, bw, a, χ, γ, γ + μ, k, S, I, W, R] + correct = OrderedDict(f => true for f in funcs_to_check) + @test isequal( + correct, + assess_local_identifiability( + de; + measured_quantities = measured_quantities, + funcs_to_check = funcs_to_check, + ), + ) + + # checking ME identifiability + funcs_to_check = [μ, bi, bw, a, χ, γ, γ + μ, k] + correct = OrderedDict(f => true for f in funcs_to_check) + @test isequal( + (correct, 1), + assess_local_identifiability( + de; + measured_quantities = measured_quantities, + funcs_to_check = funcs_to_check, + prob_threshold = 0.99, + type = :ME, + ), + ) + + # checking identifiabile functions + correct = [a, bw, χ, bi, k, γ, μ] + result = + find_identifiable_functions(de, measured_quantities = measured_quantities) + @test isequal(Set(correct), Set(result)) + + # -------------------------------------------------------------------------- + @parameters mu bi bw a xi gm k + @variables t S(t) I(t) W(t) R(t) y(t) [output = true] + + eqs = [ + D(S) ~ mu - bi * S * I - bw * S * W - mu * S + a * R, + D(I) ~ bw * S * W + bi * S * I - (gm + mu) * I, + D(W) ~ xi * (I - W), + D(R) ~ gm * I - (mu + a) * R, + y ~ k * I, + ] + de = ODESystem(eqs, t, name = :TestSIWR) + # check all parameters (default) + @test isequal(true, all(values(assess_local_identifiability(de)))) + + @test isequal( + true, + all( + values( + assess_local_identifiability(de; measured_quantities = [y ~ k * I]), ), - ) - - # ---------- - - @parameters a01 a21 a12 - @variables t x0(t) x1(t) y1(t) - D = Differential(t) - using SpecialFunctions - - eqs = [ - D(x0) ~ -(a01 + a21) * SpecialFunctions.erfc(x0) + a12 * x1, - D(x1) ~ a21 * x0 - a12 * x1, - ] - - de = ODESystem(eqs, t, name = :Test) - measured_quantities = [y1 ~ x0] - funcs_to_check = [a01, a21, a12, a01 * a12, a01 + a12 + a21] - correct = Dict( - a01 => :nonidentifiable, - a21 => :nonidentifiable, - a12 => :nonidentifiable, - a01 * a12 => :globally, - a01 + a12 + a21 => :globally, - ) - @test_throws ArgumentError assess_identifiability( + ), + ) + + # check specific parameters + funcs_to_check = [mu, bi, bw, a, xi, gm, gm + mu, k, S, I, W, R] + correct = OrderedDict(f => true for f in funcs_to_check) + @test isequal( + correct, + assess_local_identifiability(de; funcs_to_check = funcs_to_check), + ) + + # checking ME identifiability + funcs_to_check = [mu, bi, bw, a, xi, gm, gm + mu, k] + correct = OrderedDict(f => true for f in funcs_to_check) + @test isequal( + (correct, 1), + assess_local_identifiability( + de; + funcs_to_check = funcs_to_check, + prob_threshold = 0.99, + type = :ME, + ), + ) + + # -------------------------------------------------------------------------- + @parameters mu bi bw a xi gm k + @variables t S(t) I(t) W(t) R(t) y(t) + + eqs = [ + D(S) ~ 2.0 * mu - bi * S * I - bw * S * W - mu * S + a * R, + D(I) ~ bw * S * W + bi * S * I - (gm + mu) * I, + D(W) ~ xi * (I - 0.6 * W), + D(R) ~ gm * I - (mu + a) * R, + ] + de = ODESystem(eqs, t, name = :TestSIWR) + measured_quantities = [y ~ 1.57 * I * k] + funcs_to_check = [mu, bi, bw, a, xi, gm, mu, gm + mu, k, S, I, W, R] + correct = OrderedDict(f => true for f in funcs_to_check) + @test isequal( + correct, + assess_local_identifiability( de; measured_quantities = measured_quantities, funcs_to_check = funcs_to_check, - ) - # ---------- - @parameters a b c - @variables t x1(t) x2(t) y(t) + ), + ) + + # checking ME identifiability + funcs_to_check = [bi, bw, a, xi, gm, mu, gm + mu, k] + correct = OrderedDict(f => true for f in funcs_to_check) + @test isequal( + (correct, 1), + assess_local_identifiability( + de; + measured_quantities = measured_quantities, + funcs_to_check = funcs_to_check, + prob_threshold = 0.99, + type = :ME, + ), + ) + + # ---------- + + @parameters a01 a21 a12 + @variables t x0(t) x1(t) y1(t) + D = Differential(t) + using SpecialFunctions + + eqs = [ + D(x0) ~ -(a01 + a21) * SpecialFunctions.erfc(x0) + a12 * x1, + D(x1) ~ a21 * x0 - a12 * x1, + ] + + de = ODESystem(eqs, t, name = :Test) + measured_quantities = [y1 ~ x0] + funcs_to_check = [a01, a21, a12, a01 * a12, a01 + a12 + a21] + correct = Dict( + a01 => :nonidentifiable, + a21 => :nonidentifiable, + a12 => :nonidentifiable, + a01 * a12 => :globally, + a01 + a12 + a21 => :globally, + ) + @test_throws ArgumentError assess_identifiability( + de; + measured_quantities = measured_quantities, + funcs_to_check = funcs_to_check, + ) + # ---------- + @parameters a b c + @variables t x1(t) x2(t) y(t) + D = Differential(t) + + eqs = [D(x1) ~ -a * x1 + x2 * b / (x1 + b / (c^2 - x2)), D(x2) ~ x2 * c^2 + x1] + de = ODESystem(eqs, t, name = :Test) + measured_quantities = [y ~ x2] + correct = Dict(a => :globally, b => :globally, c => :locally) + to_check = [a, b, c] + @test isequal( + correct, + assess_identifiability( + de; + measured_quantities = measured_quantities, + funcs_to_check = to_check, + ), + ) + + # check identifiabile functions + result = + find_identifiable_functions(de, measured_quantities = measured_quantities) + correct = [b, a, c^2] + @test isequal(Set(result), Set(correct)) + + # ---------- + @parameters a b + @variables t c(t) x1(t) x2(t) y1(t) y2(t) + D = Differential(t) + + eqs = [ + D(x1) ~ -a * x1 + x2 * b / (x1 + b / (c^2 - x2)), + D(x2) ~ x2 * c^2 + x1, + D(c) ~ 0, + ] + de = ODESystem(eqs, t, name = :Test) + measured_quantities = [y1 ~ x2, y2 ~ c] + correct = OrderedDict(a => :globally, b => :globally) + to_check = [a, b] + @test isequal( + correct, + assess_identifiability( + de; + measured_quantities = measured_quantities, + funcs_to_check = to_check, + ), + ) + + #---------------------------------- + # Composable models test (from https://github.com/SciML/StructuralIdentifiability.jl/issues/162) + @variables t + function rabbits_creator(; name) + ps = @parameters α = 1.5 + vars = @variables x(t) = 1.0 z(t) = 0.0 [input = true] D = Differential(t) + equs = [D(x) ~ α^2 * x + z] - eqs = [D(x1) ~ -a * x1 + x2 * b / (x1 + b / (c^2 - x2)), D(x2) ~ x2 * c^2 + x1] - de = ODESystem(eqs, t, name = :Test) - measured_quantities = [y ~ x2] - correct = Dict(a => :globally, b => :globally, c => :locally) - to_check = [a, b, c] - @test isequal( - correct, - assess_identifiability( - de; - measured_quantities = measured_quantities, - funcs_to_check = to_check, - ), - ) - - # check identifiabile functions - result = - find_identifiable_functions(de, measured_quantities = measured_quantities) - correct = [b, a, c^2] - @test isequal(Set(result), Set(correct)) + ODESystem(equs, t, vars, ps; name = name) + end - # ---------- - @parameters a b - @variables t c(t) x1(t) x2(t) y1(t) y2(t) + function wolves_creator(; name) + ps = @parameters δ = 3.0 + vars = @variables y(t) = 1.0 q(t) = 0.0 [input = true] D = Differential(t) + equs = [D(y) ~ -δ * y + q] - eqs = [ - D(x1) ~ -a * x1 + x2 * b / (x1 + b / (c^2 - x2)), - D(x2) ~ x2 * c^2 + x1, - D(c) ~ 0, - ] - de = ODESystem(eqs, t, name = :Test) - measured_quantities = [y1 ~ x2, y2 ~ c] - correct = OrderedDict(a => :globally, b => :globally) - to_check = [a, b] - @test isequal( - correct, - assess_identifiability( - de; - measured_quantities = measured_quantities, - funcs_to_check = to_check, - ), - ) - - #---------------------------------- - # Composable models test (from https://github.com/SciML/StructuralIdentifiability.jl/issues/162) - @variables t - function rabbits_creator(; name) - ps = @parameters α = 1.5 - vars = @variables x(t) = 1.0 z(t) = 0.0 [input = true] - D = Differential(t) - equs = [D(x) ~ α^2 * x + z] - - ODESystem(equs, t, vars, ps; name = name) - end - - function wolves_creator(; name) - ps = @parameters δ = 3.0 - vars = @variables y(t) = 1.0 q(t) = 0.0 [input = true] - D = Differential(t) - equs = [D(y) ~ -δ * y + q] - - ODESystem(equs, t, vars, ps; name = name) - end - - function lotka_volterra_creator(; name) - @named wolves = wolves_creator() - @named rabbits = rabbits_creator() - - ps = @parameters β = 1.0 γ = 1.0 - D = Differential(t) - - eqs = [ - rabbits.z ~ -β * wolves.y * rabbits.x, - wolves.q ~ γ * wolves.y * rabbits.x, - ] - - ModelingToolkit.compose( - ODESystem(eqs, t, [], ps; name = name), - wolves, - rabbits, - ) - end - - function getbyname(sys, name) - println(name) - return first([ - v for v in vcat(states(sys), parameters(sys)) if - replace(string(v), "(t)" => "") == name - ]) - end - - @named ltk_mtk = lotka_volterra_creator() - simp_ltk_mtk = structural_simplify(ltk_mtk) - wolves₊δ = getbyname(simp_ltk_mtk, "wolves₊δ") - rabbits₊α = getbyname(simp_ltk_mtk, "rabbits₊α") - β = getbyname(simp_ltk_mtk, "β") - γ = getbyname(simp_ltk_mtk, "γ") - wolves₊y = getbyname(simp_ltk_mtk, "wolves₊y") - rabbits₊x = getbyname(simp_ltk_mtk, "rabbits₊x") - @variables y(t) - measured_quantities = [y ~ wolves₊y] - result = assess_identifiability( - simp_ltk_mtk, - measured_quantities = measured_quantities, - ) - correct = Dict( - rabbits₊α => :locally, - γ => :nonidentifiable, - β => :globally, - wolves₊δ => :globally, - rabbits₊x => :nonidentifiable, - wolves₊y => :globally, - ) - @test Dict(result) == correct - - #---------------------------------- - - @variables t, x(t), y(t), z(t), w(t) - @parameters a - @named sys = ODESystem([D(x) ~ a * y], t, [x], [a]; observed = [y ~ z, z ~ x]) - measured_quantities = [w ~ x] - result = assess_identifiability(sys, measured_quantities = measured_quantities) - @test result[a] == :globally - - result = - find_identifiable_functions(sys, measured_quantities = measured_quantities) - @test isequal(result, [a]) - - #---------------------------------- - - # Tensor definition case as reported in - # https://github.com/SciML/StructuralIdentifiability.jl/issues/178 - @variables t, x(t)[1:2], y(t)[1:2] - @parameters k1, k2 - - eqs = [D(x[1]) ~ -k1 * x[2], D(x[2]) ~ -k2 * x[1]] - - sys = ODESystem(eqs, t, name = :example_vector) - correct = OrderedDict(x[1] => true, x[2] => true, k1 => true, k2 => true) - @test assess_local_identifiability(sys, measured_quantities = [x[1], x[2]]) == - correct + ODESystem(equs, t, vars, ps; name = name) end - @testset "Discrete local identifiability, ModelingToolkit interface" begin - cases = [] - - @parameters α β - @variables t S(t) I(t) R(t) y(t) - D = Difference(t; dt = 1.0) - - eqs = [D(S) ~ -β * S * I, D(I) ~ β * S * I - α * I, D(R) ~ α * I] - @named sir = DiscreteSystem(eqs) - push!( - cases, - Dict( - :dds => sir, - :res => - OrderedDict(S => true, I => true, R => false, α => true, β => true), - :y => [y ~ I], - :y2 => [I], - :known_ic => Array{}[], - :to_check => Array{}[], - ), - ) + function lotka_volterra_creator(; name) + @named wolves = wolves_creator() + @named rabbits = rabbits_creator() - @parameters θ - @variables t x(t) y(t) - D = Difference(t; dt = 1.0) - - eqs = [D(x) ~ θ * x^3 - x] - - @named eqs = DiscreteSystem(eqs) - push!( - cases, - Dict( - :dds => eqs, - :res => OrderedDict(x => true, θ => true), - :y => [y ~ x], - :y2 => [x], - :known_ic => Array{}[], - :to_check => Array{}[], - ), - ) + ps = @parameters β = 1.0 γ = 1.0 + D = Differential(t) - @parameters θ β - @variables t x1(t) x2(t) y(t) - D = Difference(t; dt = 1.0) - - eqs = [D(x1) ~ x1 + x2, D(x2) ~ θ + β] - - @named eqs = DiscreteSystem(eqs) - push!( - cases, - Dict( - :dds => eqs, - :res => OrderedDict(x1 => true, x2 => true, θ => false, β => false), - :y => [y ~ x1], - :y2 => [x1], - :known_ic => Array{}[], - :to_check => Array{}[], - ), - ) + eqs = [ + rabbits.z ~ -β * wolves.y * rabbits.x, + wolves.q ~ γ * wolves.y * rabbits.x, + ] - @parameters a b c d - @variables t x1(t) x2(t) u(t) y2(t) - D = Difference(t; dt = 1.0) - - eqs = [D(x1) ~ a * x1 - b * x1 * x2 + u, D(x2) ~ -c * x2 + d * x1 * x2] - - @named lv = DiscreteSystem(eqs) - push!( - cases, - Dict( - :dds => lv, - :res => OrderedDict( - x1 => true, - x2 => false, - a => true, - b => false, - c => true, - d => true, - ), - :y => [y ~ x1], - :y2 => [x1], - :known_ic => Array{}[], - :to_check => Array{}[], - ), + ModelingToolkit.compose( + ODESystem(eqs, t, [], ps; name = name), + wolves, + rabbits, ) + end - push!( - cases, - Dict( - :dds => lv, - :res => OrderedDict(b * x2 => true), - :y => [y ~ x1], - :y2 => [x1], - :known_ic => Array{}[], - :to_check => [b * x2], - ), - ) + function getbyname(sys, name) + println(name) + return first([ + v for v in vcat(states(sys), parameters(sys)) if + replace(string(v), "(t)" => "") == name + ]) + end - push!( - cases, - Dict( - :dds => lv, - :res => OrderedDict( - x1 => true, - x2 => true, - a => true, - b => true, - c => true, - d => true, - ), - :y => [y ~ x1, y2 ~ x1 / x2], - :y2 => [x1, x1 / x2], - :known_ic => Array{}[], - :to_check => Array{}[], - ), - ) + @named ltk_mtk = lotka_volterra_creator() + simp_ltk_mtk = structural_simplify(ltk_mtk) + wolves₊δ = getbyname(simp_ltk_mtk, "wolves₊δ") + rabbits₊α = getbyname(simp_ltk_mtk, "rabbits₊α") + β = getbyname(simp_ltk_mtk, "β") + γ = getbyname(simp_ltk_mtk, "γ") + wolves₊y = getbyname(simp_ltk_mtk, "wolves₊y") + rabbits₊x = getbyname(simp_ltk_mtk, "rabbits₊x") + @variables y(t) + measured_quantities = [y ~ wolves₊y] + result = assess_identifiability( + simp_ltk_mtk, + measured_quantities = measured_quantities, + ) + correct = Dict( + rabbits₊α => :locally, + γ => :nonidentifiable, + β => :globally, + wolves₊δ => :globally, + rabbits₊x => :nonidentifiable, + wolves₊y => :globally, + ) + @test Dict(result) == correct + + #---------------------------------- + + @variables t, x(t), y(t), z(t), w(t) + @parameters a + @named sys = ODESystem([D(x) ~ a * y], t, [x], [a]; observed = [y ~ z, z ~ x]) + measured_quantities = [w ~ x] + result = assess_identifiability(sys, measured_quantities = measured_quantities) + @test result[a] == :globally + + result = + find_identifiable_functions(sys, measured_quantities = measured_quantities) + @test isequal(result, [a]) + + #---------------------------------- + + # Tensor definition case as reported in + # https://github.com/SciML/StructuralIdentifiability.jl/issues/178 + @variables t, x(t)[1:2], y(t)[1:2] + @parameters k1, k2 + + eqs = [D(x[1]) ~ -k1 * x[2], D(x[2]) ~ -k2 * x[1]] + + sys = ODESystem(eqs, t, name = :example_vector) + correct = OrderedDict(x[1] => true, x[2] => true, k1 => true, k2 => true) + @test assess_local_identifiability(sys, measured_quantities = [x[1], x[2]]) == + correct + end - push!( - cases, - Dict( - :dds => lv, - :res => OrderedDict( - substitute(x1, Dict(t => 0)) => true, - substitute(x2, Dict(t => 0)) => true, - a => true, - b => true, - c => true, - d => true, - ), - :y => [y ~ x1], - :y2 => [x1], - :known_ic => [x2], - :to_check => Array{}[], + @testset "Discrete local identifiability, ModelingToolkit interface" begin + cases = [] + + @parameters α β + @variables t S(t) I(t) R(t) y(t) + D = Difference(t; dt = 1.0) + + eqs = [D(S) ~ -β * S * I, D(I) ~ β * S * I - α * I, D(R) ~ α * I] + @named sir = DiscreteSystem(eqs) + push!( + cases, + Dict( + :dds => sir, + :res => + OrderedDict(S => true, I => true, R => false, α => true, β => true), + :y => [y ~ I], + :y2 => [I], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + + @parameters θ + @variables t x(t) y(t) + D = Difference(t; dt = 1.0) + + eqs = [D(x) ~ θ * x^3 - x] + + @named eqs = DiscreteSystem(eqs) + push!( + cases, + Dict( + :dds => eqs, + :res => OrderedDict(x => true, θ => true), + :y => [y ~ x], + :y2 => [x], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + + @parameters θ β + @variables t x1(t) x2(t) y(t) + D = Difference(t; dt = 1.0) + + eqs = [D(x1) ~ x1 + x2, D(x2) ~ θ + β] + + @named eqs = DiscreteSystem(eqs) + push!( + cases, + Dict( + :dds => eqs, + :res => OrderedDict(x1 => true, x2 => true, θ => false, β => false), + :y => [y ~ x1], + :y2 => [x1], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + + @parameters a b c d + @variables t x1(t) x2(t) u(t) y2(t) + D = Difference(t; dt = 1.0) + + eqs = [D(x1) ~ a * x1 - b * x1 * x2 + u, D(x2) ~ -c * x2 + d * x1 * x2] + + @named lv = DiscreteSystem(eqs) + push!( + cases, + Dict( + :dds => lv, + :res => OrderedDict( + x1 => true, + x2 => false, + a => true, + b => false, + c => true, + d => true, ), - ) - - # Example 1 from https://doi.org/10.1016/j.automatica.2008.03.019 - @parameters theta1 theta2 - @variables t x1(t) x2(t) u(t) y(t) - D = Difference(t; dt = 1.0) - - eqs = [D(x1) ~ theta1 * x1 + x2, D(x2) ~ (1 - theta2) * x1 + x2^2 + u - x2] - - @named abmd1 = DiscreteSystem(eqs) - push!( - cases, - Dict( - :dds => abmd1, - :res => - OrderedDict(x1 => true, x2 => true, theta1 => true, theta2 => true), - :y => [y ~ x1], - :y2 => [x1], - :known_ic => Array{}[], - :to_check => Array{}[], + :y => [y ~ x1], + :y2 => [x1], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + + push!( + cases, + Dict( + :dds => lv, + :res => OrderedDict(b * x2 => true), + :y => [y ~ x1], + :y2 => [x1], + :known_ic => Array{}[], + :to_check => [b * x2], + ), + ) + + push!( + cases, + Dict( + :dds => lv, + :res => OrderedDict( + x1 => true, + x2 => true, + a => true, + b => true, + c => true, + d => true, ), - ) - - # Example 2 from https://doi.org/10.1016/j.automatica.2008.03.019 - @parameters theta1 theta2 theta3 - @variables t x1(t) x2(t) u(t) y(t) y2(t) - D = Difference(t; dt = 1.0) - - eqs = [D(x1) ~ theta1 * x1^2 + theta2 * x2 + u - x1, D(x2) ~ theta3 * x1 - x2] - - @named abmd2 = DiscreteSystem(eqs) - push!( - cases, - Dict( - :dds => abmd2, - :res => OrderedDict( - x1 => true, - x2 => false, - theta1 => true, - theta2 => false, - theta3 => false, - ), - :y => [y ~ x1], - :y2 => [x1], - :known_ic => Array{}[], - :to_check => Array{}[], + :y => [y ~ x1, y2 ~ x1 / x2], + :y2 => [x1, x1 / x2], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + + push!( + cases, + Dict( + :dds => lv, + :res => OrderedDict( + substitute(x1, Dict(t => 0)) => true, + substitute(x2, Dict(t => 0)) => true, + a => true, + b => true, + c => true, + d => true, ), - ) - push!( - cases, - Dict( - :dds => abmd2, - :res => Dict( - x1 => true, - x2 => true, - theta1 => true, - theta2 => true, - theta3 => true, - ), - :y => [y ~ x1, y2 ~ x2], - :y2 => [x1, x2], - :known_ic => Array{}[], - :to_check => Array{}[], + :y => [y ~ x1], + :y2 => [x1], + :known_ic => [x2], + :to_check => Array{}[], + ), + ) + + # Example 1 from https://doi.org/10.1016/j.automatica.2008.03.019 + @parameters theta1 theta2 + @variables t x1(t) x2(t) u(t) y(t) + D = Difference(t; dt = 1.0) + + eqs = [D(x1) ~ theta1 * x1 + x2, D(x2) ~ (1 - theta2) * x1 + x2^2 + u - x2] + + @named abmd1 = DiscreteSystem(eqs) + push!( + cases, + Dict( + :dds => abmd1, + :res => + OrderedDict(x1 => true, x2 => true, theta1 => true, theta2 => true), + :y => [y ~ x1], + :y2 => [x1], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + + # Example 2 from https://doi.org/10.1016/j.automatica.2008.03.019 + @parameters theta1 theta2 theta3 + @variables t x1(t) x2(t) u(t) y(t) y2(t) + D = Difference(t; dt = 1.0) + + eqs = [D(x1) ~ theta1 * x1^2 + theta2 * x2 + u - x1, D(x2) ~ theta3 * x1 - x2] + + @named abmd2 = DiscreteSystem(eqs) + push!( + cases, + Dict( + :dds => abmd2, + :res => OrderedDict( + x1 => true, + x2 => false, + theta1 => true, + theta2 => false, + theta3 => false, ), - ) - - @parameters a b - @variables t x1(t) y(t) - D = Difference(t; dt = 1.0) - - eqs = [D(x1) ~ a] - - @named kic = DiscreteSystem(eqs) - push!( - cases, - Dict( - :dds => kic, - :res => OrderedDict(x1 => false, a => true, b => false), - :y => [y ~ x1 + b], - :y2 => [x1 + b], - :known_ic => Array{}[], - :to_check => Array{}[], + :y => [y ~ x1], + :y2 => [x1], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + push!( + cases, + Dict( + :dds => abmd2, + :res => Dict( + x1 => true, + x2 => true, + theta1 => true, + theta2 => true, + theta3 => true, ), - ) - push!( - cases, - Dict( - :dds => kic, - :res => OrderedDict( - substitute(x1, Dict(t => 0)) => true, - a => true, - b => true, - ), - :y => [y ~ x1 + b], - :y2 => [x1 + b], - :known_ic => [x1], - :to_check => Array{}[], + :y => [y ~ x1, y2 ~ x2], + :y2 => [x1, x2], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + + @parameters a b + @variables t x1(t) y(t) + D = Difference(t; dt = 1.0) + + eqs = [D(x1) ~ a] + + @named kic = DiscreteSystem(eqs) + push!( + cases, + Dict( + :dds => kic, + :res => OrderedDict(x1 => false, a => true, b => false), + :y => [y ~ x1 + b], + :y2 => [x1 + b], + :known_ic => Array{}[], + :to_check => Array{}[], + ), + ) + push!( + cases, + Dict( + :dds => kic, + :res => OrderedDict( + substitute(x1, Dict(t => 0)) => true, + a => true, + b => true, ), - ) - - for c in cases - @test assess_local_identifiability( - c[:dds]; - measured_quantities = c[:y], - known_ic = c[:known_ic], - funcs_to_check = c[:to_check], - ) == c[:res] - @test assess_local_identifiability( - c[:dds]; - measured_quantities = c[:y2], - known_ic = c[:known_ic], - funcs_to_check = c[:to_check], - ) == c[:res] - end + :y => [y ~ x1 + b], + :y2 => [x1 + b], + :known_ic => [x1], + :to_check => Array{}[], + ), + ) + + for c in cases + @test assess_local_identifiability( + c[:dds]; + measured_quantities = c[:y], + known_ic = c[:known_ic], + funcs_to_check = c[:to_check], + ) == c[:res] + @test assess_local_identifiability( + c[:dds]; + measured_quantities = c[:y2], + known_ic = c[:known_ic], + funcs_to_check = c[:to_check], + ) == c[:res] end end end diff --git a/test/extract_coefficients.jl b/test/extract_coefficients.jl index a6092a23..24d195db 100644 --- a/test/extract_coefficients.jl +++ b/test/extract_coefficients.jl @@ -1,61 +1,59 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Coefficient extraction for rational fucntions" begin - R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]) - C = extract_coefficients_ratfunc( - (x^2 + y * z - y^2 * z^3 + 3 * x * z^3) // (x + y + z + z^2 * (x^2 + 1)), - [z], - ) +@testset "Coefficient extraction for rational fucntions" begin + R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]) + C = extract_coefficients_ratfunc( + (x^2 + y * z - y^2 * z^3 + 3 * x * z^3) // (x + y + z + z^2 * (x^2 + 1)), + [z], + ) - @test Set(C) == Set([ - one(R) // 1, - (3 * x - y^2) // 1, - y // 1, - x^2 // 1, - (x + y) // 1, - (x^2 + 1) // 1, - ]) + @test Set(C) == Set([ + one(R) // 1, + (3 * x - y^2) // 1, + y // 1, + x^2 // 1, + (x + y) // 1, + (x^2 + 1) // 1, + ]) - R, (x, y) = polynomial_ring(QQ, ["x", "y"]) - f = (x^2 + y^2) // (1 - x - 3 * y) - @test Set(extract_coefficients_ratfunc(f, Vector{Nemo.QQMPolyRingElem}())) == - Set([f, one(R) // 1]) + R, (x, y) = polynomial_ring(QQ, ["x", "y"]) + f = (x^2 + y^2) // (1 - x - 3 * y) + @test Set(extract_coefficients_ratfunc(f, Vector{Nemo.QQMPolyRingElem}())) == + Set([f, one(R) // 1]) - R, (x, y, u, v) = polynomial_ring(QQ, ["x", "y", "u", "v"]) - C = extract_coefficients_ratfunc( - (x + (y + 3) * u * v + y^2 * v^3) // (u + 3 * v - (x^2 + y^2) * u^2), - [u, v], - ) - @test Set(C) == Set([ - x // 1, - (y + 3) // 1, - y^2 // 1, - one(R) // 1, - 3 * one(R) // 1, - -(x^2 + y^2) // 1, - ]) - end + R, (x, y, u, v) = polynomial_ring(QQ, ["x", "y", "u", "v"]) + C = extract_coefficients_ratfunc( + (x + (y + 3) * u * v + y^2 * v^3) // (u + 3 * v - (x^2 + y^2) * u^2), + [u, v], + ) + @test Set(C) == Set([ + x // 1, + (y + 3) // 1, + y^2 // 1, + one(R) // 1, + 3 * one(R) // 1, + -(x^2 + y^2) // 1, + ]) +end - @testset "Coefficient extraction for polynomials" begin - R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]) - C = extract_coefficients((y + z + 8), [x]) - R_coef = parent(first(values(C))) - y, z = gens(R_coef) - @test symbols(R_coef) == [:y, :z] - @test C == Dict([0] => y + z + 8) +@testset "Coefficient extraction for polynomials" begin + R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]) + C = extract_coefficients((y + z + 8), [x]) + R_coef = parent(first(values(C))) + y, z = gens(R_coef) + @test symbols(R_coef) == [:y, :z] + @test C == Dict([0] => y + z + 8) - R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]) - C = extract_coefficients((x^2 + y * z - y^2 * z^3 + 3 * x * z^3), [z]) - R_coef = parent(first(values(C))) - x, y = gens(R_coef) - @test symbols(R_coef) == [:x, :y] - @test C == Dict([3] => 3x - y^2, [1] => y, [0] => x^2) + R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]) + C = extract_coefficients((x^2 + y * z - y^2 * z^3 + 3 * x * z^3), [z]) + R_coef = parent(first(values(C))) + x, y = gens(R_coef) + @test symbols(R_coef) == [:x, :y] + @test C == Dict([3] => 3x - y^2, [1] => y, [0] => x^2) - R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]) - C = extract_coefficients((x^2 + y * z - y^2 * z^3 + 3 * x * z^3), [x, z]) - R_coef = parent(first(values(C))) - y = gens(R_coef)[1] - @test symbols(R_coef) == [:y] - @test C == - Dict([1, 3] => R_coef(3), [2, 0] => R_coef(1), [0, 3] => -y^2, [0, 1] => y) - end + R, (x, y, z) = polynomial_ring(QQ, ["x", "y", "z"]) + C = extract_coefficients((x^2 + y * z - y^2 * z^3 + 3 * x * z^3), [x, z]) + R_coef = parent(first(values(C))) + y = gens(R_coef)[1] + @test symbols(R_coef) == [:y] + @test C == + Dict([1, 3] => R_coef(3), [2, 0] => R_coef(1), [0, 3] => -y^2, [0, 1] => y) end diff --git a/test/find_leader.jl b/test/find_leader.jl index de940963..5b12feed 100644 --- a/test/find_leader.jl +++ b/test/find_leader.jl @@ -1,26 +1,24 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Finding leader" begin - ode = @ODEmodel( - x1'(t) = x3(t), - x2'(t) = a * x2(t), - x3'(t) = x1(t), - y1(t) = x1(t), - y2(t) = x2(t) + u(t) - ) - ioeqs = find_ioequations(ode) - println("IOEQS: ", ioeqs) - pbr = PBRepresentation(ode, ioeqs) +@testset "Finding leader" begin + ode = @ODEmodel( + x1'(t) = x3(t), + x2'(t) = a * x2(t), + x3'(t) = x1(t), + y1(t) = x1(t), + y2(t) = x2(t) + u(t) + ) + ioeqs = find_ioequations(ode) + println("IOEQS: ", ioeqs) + pbr = PBRepresentation(ode, ioeqs) - R, (y1_0, y1_1, y1_2, y2_0, y2_1, y2_2) = Nemo.polynomial_ring( - Nemo.QQ, - ["y1(t)_0", "y1(t)_1", "y1(t)_2", "y2(t)_0", "y2(t)_1", "y2(t)_2"], - ) - @test find_leader([a, x1, x2, u], pbr) == nothing - @test find_leader([a, x1, y1_0, x2], pbr) == y1_0 - @test find_leader([y1_0, y1_1, y2_0, y2_1], pbr) == y2_1 - l = find_leader([y1_2, y2_1], pbr) - @test (pbr.y_names[1] == "y1(t)" && l == y2_1) || - (pbr.y_names[1] == "y2(t)" && l == y1_2) - @test find_leader([y1_2, y2_0], pbr) == y1_2 - end + R, (y1_0, y1_1, y1_2, y2_0, y2_1, y2_2) = Nemo.polynomial_ring( + Nemo.QQ, + ["y1(t)_0", "y1(t)_1", "y1(t)_2", "y2(t)_0", "y2(t)_1", "y2(t)_2"], + ) + @test find_leader([a, x1, x2, u], pbr) == nothing + @test find_leader([a, x1, y1_0, x2], pbr) == y1_0 + @test find_leader([y1_0, y1_1, y2_0, y2_1], pbr) == y2_1 + l = find_leader([y1_2, y2_1], pbr) + @test (pbr.y_names[1] == "y1(t)" && l == y2_1) || + (pbr.y_names[1] == "y2(t)" && l == y1_2) + @test find_leader([y1_2, y2_0], pbr) == y1_2 end diff --git a/test/identifiability.jl b/test/identifiability.jl index 1b51ed5a..cec848d1 100644 --- a/test/identifiability.jl +++ b/test/identifiability.jl @@ -1,237 +1,235 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Assessing identifiability" begin - test_cases = [] - - # 2-compartiment model - - ode = @ODEmodel( - x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t), - x1'(t) = a21 * x0(t) - a12 * x1(t), - y(t) = x0(t) - ) - funcs_to_test = - [a01, a21, a12, a01 * a12, a01 + a12 + a21, (a01 + a12 + a21) // (a01 * a12)] - correct = [ - :nonidentifiable, - :nonidentifiable, - :nonidentifiable, - :globally, - :globally, - :globally, - ] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - # No parameters no worry - - ode = @ODEmodel(x1'(t) = x1, x2'(t) = x2, y(t) = x1 + x2(t)) - funcs_to_test = [x1, x2, x1 + x2] - correct = [:nonidentifiable, :nonidentifiable, :globally] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - # Also test when `funcs_to_test` is empty! - funcs_to_test = Vector{typeof(x1)}() - correct = OrderedDict(x1 => :nonidentifiable, x2 => :nonidentifiable) - push!(test_cases, Dict(:ode => ode, :funcs => funcs_to_test, :correct => correct)) - - #-------------------------------------------------------------------------- - - ode = @ODEmodel( - x0'(t) = a * x0(t) - b * x0(t) * x1(t) + u(t), - x1'(t) = c * x1(t) + d * x0(t) * x1(t), - y(t) = x0(t) - ) - funcs_to_test = [a, b, c, d, b * x1, x0, x1] - correct = [ - :globally, - :nonidentifiable, - :globally, - :globally, - :globally, - :globally, - :nonidentifiable, - ] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - - ode = @ODEmodel( - S'(t) = mu - bi * S(t) * I(t) - bw * S(t) * W(t) - mu * S(t) + a * R(t), - I'(t) = bw * S(t) * W(t) + bi * S(t) * I(t) - (gam + mu) * I(t), - W'(t) = xi * (I(t) - W(t)), - R'(t) = gam * I(t) - (mu + a) * R(t), - y(t) = k * I(t) - ) - funcs_to_test = [mu, bi, bw, a, xi, gam, mu, gam + mu, k] - correct = [:globally for _ in funcs_to_test] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - - ode = @ODEmodel( - x1'(t) = -b * x1(t) + 1 / (c + x4(t)), - x2'(t) = alpha * x1(t) - beta * x2(t), - x3'(t) = gama * x2(t) - delta * x3(t), - x4'(t) = sigma * x4(t) * (gama * x2(t) - delta * x3(t)) / x3(t), - y(t) = x1(t) - ) - funcs_to_test = [b, c, alpha, beta, delta, gama, beta + delta, beta * delta] - correct = [ - :globally, - :globally, - :nonidentifiable, - :locally, - :locally, - :nonidentifiable, - :globally, - :globally, - ] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - - ode = @ODEmodel( - x1'(t) = (1 + x1(t)^2) // 2, - x2'(t) = (1 - x1(t)^2) // (1 + x1(t)^2), - y1(t) = 2 * x1(t) // (b * (1 + x1(t)^2)), - y2(t) = x2(t) - ) - funcs_to_test = [b, x1, x2] - correct = [:globally, :globally, :globally] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - - ode = @ODEmodel( - x1'(t) = -a1 * x1(t) + a21 * x2(t), - x2'(t) = -a2 * x2(t) - a21 * x2(t), - y1(t) = x1(t) - ) - funcs_to_test = [a1, a2, a21] - correct = [:locally, :nonidentifiable, :nonidentifiable] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - - ode = @ODEmodel( - x1'(t) = -a1 * x1(t) + a21 * x2(t), - x2'(t) = -a2 * x2(t) - a21 * x2(t) + u(t), - y1(t) = x1(t) - ) - funcs_to_test = [a1, a2, a21, a2 + a1, a2 * (a1 - a21)] - correct = [:locally, :locally, :globally, :globally, :globally] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - - ode = @ODEmodel( - x1'(t) = -(a21 + a31 + a01) * x1(t) + a12 * x2(t) + a13 * x3(t) + u(t), - x2'(t) = a21 * x1(t) - a12 * x2(t), - x3'(t) = a31 * x1(t) - a13 * x3(t), - y(t) = x1(t) - ) - funcs_to_test = [x1, x2, x3, a31 + a21, (a21 - a31) // (a12 - a13)] - correct = [:globally, :locally, :locally, :globally, :globally] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - - ode = @ODEmodel( - S'(t) = -b * S(t) * In(t) / N, - E'(t) = b * S(t) * In(t) / N - nu * E(t), - In'(t) = nu * E(t) - a * In(t), - y1(t) = In(t), - y2(t) = N - ) - funcs_to_test = [b, N, In, a, nu, S, E, a * nu, a + nu] - correct = [ - :globally, - :globally, - :globally, - :locally, - :locally, - :locally, - :locally, - :globally, - :globally, - ] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) - - #-------------------------------------------------------------------------- - - for case in test_cases - result = assess_identifiability(case[:ode], funcs_to_check = case[:funcs]) - @test result == case[:correct] - end +@testset "Assessing identifiability" begin + test_cases = [] + + # 2-compartiment model + + ode = @ODEmodel( + x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t), + x1'(t) = a21 * x0(t) - a12 * x1(t), + y(t) = x0(t) + ) + funcs_to_test = + [a01, a21, a12, a01 * a12, a01 + a12 + a21, (a01 + a12 + a21) // (a01 * a12)] + correct = [ + :nonidentifiable, + :nonidentifiable, + :nonidentifiable, + :globally, + :globally, + :globally, + ] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + # No parameters no worry + + ode = @ODEmodel(x1'(t) = x1, x2'(t) = x2, y(t) = x1 + x2(t)) + funcs_to_test = [x1, x2, x1 + x2] + correct = [:nonidentifiable, :nonidentifiable, :globally] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + # Also test when `funcs_to_test` is empty! + funcs_to_test = Vector{typeof(x1)}() + correct = OrderedDict(x1 => :nonidentifiable, x2 => :nonidentifiable) + push!(test_cases, Dict(:ode => ode, :funcs => funcs_to_test, :correct => correct)) + + #-------------------------------------------------------------------------- + + ode = @ODEmodel( + x0'(t) = a * x0(t) - b * x0(t) * x1(t) + u(t), + x1'(t) = c * x1(t) + d * x0(t) * x1(t), + y(t) = x0(t) + ) + funcs_to_test = [a, b, c, d, b * x1, x0, x1] + correct = [ + :globally, + :nonidentifiable, + :globally, + :globally, + :globally, + :globally, + :nonidentifiable, + ] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + + ode = @ODEmodel( + S'(t) = mu - bi * S(t) * I(t) - bw * S(t) * W(t) - mu * S(t) + a * R(t), + I'(t) = bw * S(t) * W(t) + bi * S(t) * I(t) - (gam + mu) * I(t), + W'(t) = xi * (I(t) - W(t)), + R'(t) = gam * I(t) - (mu + a) * R(t), + y(t) = k * I(t) + ) + funcs_to_test = [mu, bi, bw, a, xi, gam, mu, gam + mu, k] + correct = [:globally for _ in funcs_to_test] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + + ode = @ODEmodel( + x1'(t) = -b * x1(t) + 1 / (c + x4(t)), + x2'(t) = alpha * x1(t) - beta * x2(t), + x3'(t) = gama * x2(t) - delta * x3(t), + x4'(t) = sigma * x4(t) * (gama * x2(t) - delta * x3(t)) / x3(t), + y(t) = x1(t) + ) + funcs_to_test = [b, c, alpha, beta, delta, gama, beta + delta, beta * delta] + correct = [ + :globally, + :globally, + :nonidentifiable, + :locally, + :locally, + :nonidentifiable, + :globally, + :globally, + ] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + + ode = @ODEmodel( + x1'(t) = (1 + x1(t)^2) // 2, + x2'(t) = (1 - x1(t)^2) // (1 + x1(t)^2), + y1(t) = 2 * x1(t) // (b * (1 + x1(t)^2)), + y2(t) = x2(t) + ) + funcs_to_test = [b, x1, x2] + correct = [:globally, :globally, :globally] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + + ode = @ODEmodel( + x1'(t) = -a1 * x1(t) + a21 * x2(t), + x2'(t) = -a2 * x2(t) - a21 * x2(t), + y1(t) = x1(t) + ) + funcs_to_test = [a1, a2, a21] + correct = [:locally, :nonidentifiable, :nonidentifiable] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + + ode = @ODEmodel( + x1'(t) = -a1 * x1(t) + a21 * x2(t), + x2'(t) = -a2 * x2(t) - a21 * x2(t) + u(t), + y1(t) = x1(t) + ) + funcs_to_test = [a1, a2, a21, a2 + a1, a2 * (a1 - a21)] + correct = [:locally, :locally, :globally, :globally, :globally] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + + ode = @ODEmodel( + x1'(t) = -(a21 + a31 + a01) * x1(t) + a12 * x2(t) + a13 * x3(t) + u(t), + x2'(t) = a21 * x1(t) - a12 * x2(t), + x3'(t) = a31 * x1(t) - a13 * x3(t), + y(t) = x1(t) + ) + funcs_to_test = [x1, x2, x3, a31 + a21, (a21 - a31) // (a12 - a13)] + correct = [:globally, :locally, :locally, :globally, :globally] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + + ode = @ODEmodel( + S'(t) = -b * S(t) * In(t) / N, + E'(t) = b * S(t) * In(t) / N - nu * E(t), + In'(t) = nu * E(t) - a * In(t), + y1(t) = In(t), + y2(t) = N + ) + funcs_to_test = [b, N, In, a, nu, S, E, a * nu, a + nu] + correct = [ + :globally, + :globally, + :globally, + :locally, + :locally, + :locally, + :locally, + :globally, + :globally, + ] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) + + #-------------------------------------------------------------------------- + + for case in test_cases + result = assess_identifiability(case[:ode], funcs_to_check = case[:funcs]) + @test result == case[:correct] end end diff --git a/test/identifiable_functions.jl b/test/identifiable_functions.jl index 04376531..4c2946cc 100644 --- a/test/identifiable_functions.jl +++ b/test/identifiable_functions.jl @@ -1,1007 +1,1005 @@ -if GROUP == "All" || GROUP == "Core" - # For each ODE system we check the equality (in terms of fields of rational - # functions) of the true set of identifiable functions and the obtained - # simplified set - test_cases = [] - - ### - - ode = StructuralIdentifiability.@ODEmodel(x'(t) = a * x(t) + u(t), y(t) = x(t)) - ident_funcs = [a] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - ode = StructuralIdentifiability.@ODEmodel(x1'(t) = a, x2'(t) = -a, y(t) = x1 + x2) - ident_funcs = [] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Parameter a is not identifiable, and neither are any combinations thereof. - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = x2(t) - a, - x2'(t) = x1(t) + a, - y(t) = x1(t) + x2(t) - ) - ident_funcs = - Vector{StructuralIdentifiability.AbstractAlgebra.Generic.Frac{typeof(x1)}}() - - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Example 2 from - # "On Global Identifiability for Arbitrary Model Parametrizations", - # DOI: 10.1016/0005-1098(94)90029-9 - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = Θ * x2(t)^2, - x2'(t) = u(t), - y(t) = x1(t) - ) - ident_funcs = [Θ] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Example 4 from - # "On Global Identifiability for Arbitrary Model Parametrizations", - # DOI: 10.1016/0005-1098(94)90029-9 - ode = StructuralIdentifiability.@ODEmodel( - x'(t) = (-V_m * x(t)) / (k_m + x(t)) + k01 * x(t), - y(t) = c * x(t) - ) - ident_funcs = [k01, k_m // V_m, V_m * c] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Parameters b and c enter the io equations only as the product b * c. - ode = StructuralIdentifiability.@ODEmodel(x'(t) = a * x(t) + b * u(t), y(t) = c * x(t)) - ident_funcs = [b * c, a] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # 2-compartiment model - ode = StructuralIdentifiability.@ODEmodel( - x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t), - x1'(t) = a21 * x0(t) - a12 * x1(t), - y(t) = x0(t) - ) - ident_funcs = [(a01 * a12), (a01 + a12 + a21)] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # TODO: uncomment when identifiability can handle models with no states - # ode = StructuralIdentifiability.@ODEmodel( - # y(t) = a*u(t) - # ) - # ident_funcs = [(a01 * a12), (a01 + a12 + a21)] - # push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Example 2 from - # "On Structural Identifiability", - # DOI: https://doi.org/10.1016/0025-5564(70)90132-X - # - # More or less the same 2-compartmental model as the one given above - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = -(k1 + k2) * x1(t) + k3 * x2(t) + u(t), - x2'(t) = k2 * x1(t) - (k3 + k4) * x2(t), - y(t) = x1(t) - ) - ident_funcs = [(k1 + k2), (k1 + k2 + k3 + k4), ((k1 + k2) * (k3 + k4) - k2 * k3)] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Diagonal with simple spectrum and observable states - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = λ1 * x1(t) + β1 * u1(t), - x2'(t) = λ2 * x2(t) + β2 * u2(t), - x3'(t) = λ3 * x3(t) + β3 * u3(t), - y(t) = x1(t) + x2(t) + x3(t) - ) - ident_funcs = [λ1, λ2, λ3, β1, β2, β3] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # 3 compartments: - # x1 <--> x2 <--> x3 - # If we observe x1 and control x1, then all parameters are identifiable - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = -a1 * x1(t) + b1 * x2(t) + u(t), - x2'(t) = -(a2 + b1) * x2(t) + a1 * x1(t) + b2 * x3(t), - x3'(t) = -b2 * x3(t) + a2 * x2(t), - y(t) = x1(t) - ) - ident_funcs = [a1, a2, b1, b2] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Example 3 from - # "On Structural Identifiability", - # DOI: https://doi.org/10.1016/0025-5564(70)90132-X - # - # 3 compartments: - # x1 <--> x2 <--> x3 - # If we observe x1 and control x3, then only some functions of parameters - # are identifiable - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = -a1 * x1(t) + b1 * x2(t), - x2'(t) = -(a2 + b1) * x2(t) + a1 * x1(t) + b2 * x3(t), - x3'(t) = -b2 * x3(t) + a2 * x2(t) + u(t), - y(t) = x1(t) - ) - ident_funcs = [b1 * b2, a1 + a2 + b1 + b2, a1 * a2 + a1 * b2] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Example 3 from - # "On the identifiability and distinguishability of nonlinear parametric - # models", - # DOI: https://doi.org/10.1016/0378-4754(95)00123-9 - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = p1 * x1^2 + p2 * x1 * x2, - x2'(t) = p3 * x1^2 + p4 * x1 * x2, - y(t) = x1 - ) - ident_funcs = [p1 + p4, p2 * p3 - p4 * p1] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Goowdin oscillator - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = -b * x1(t) + 1 / (c + x4(t)), - x2'(t) = alpha * x1(t) - beta * x2(t), - x3'(t) = gama * x2(t) - delta * x3(t), - x4'(t) = sigma * x4(t) * (gama * x2(t) - delta * x3(t)) / x3(t), - y(t) = x1(t) - ) - ident_funcs = [sigma, delta + beta, c, b, delta * beta] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # SIRS forced - ode = StructuralIdentifiability.@ODEmodel( - s'(t) = mu - mu * s(t) - b0 * (1 + b1 * x1(t)) * i(t) * s(t) + g * r(t), - i'(t) = b0 * (1 + b1 * x1(t)) * i(t) * s(t) - (nu + mu) * i(t), - r'(t) = nu * i(t) - (mu + g) * r(t), - x1'(t) = -M * x2(t), - x2'(t) = M * x1(t), - y1(t) = i(t), - y2(t) = r(t) - ) - ident_funcs = [g, mu, b0, nu, M^2] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # SEIR_1_io - ode = StructuralIdentifiability.@ODEmodel( - S'(t) = -beta * S(t) * I(t), - E'(t) = beta * S(t) * I(t) - v * E(t), - I'(t) = v * E(t) - psi * I(t) - (1 - psi) * gamma * I(t), - R'(t) = gamma * Q(t) + (1 - psi) * gamma * I(t), - Q'(t) = -gamma * Q(t) + psi * I(t), - y1(t) = Q(t) - ) - ident_funcs = [gamma, beta // psi, gamma * psi - v - psi, gamma * psi - v * psi] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Bilirubin2_io. - # Regression test: failed before, as the total degrees were being estimated - # incorrectly in the interpolation - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = - -(k21 + k31 + k41 + k01) * x1(t) + - k12 * x2(t) + - k13 * x3(t) + - k14 * x4(t) + - u(t), - x2'(t) = k21 * x1(t) - k12 * x2(t), - x3'(t) = k31 * x1(t) - k13 * x3(t), - x4'(t) = k41 * x1(t) - k14 * x4(t), - y1(t) = x1(t) - ) - ident_funcs = [ - k01 // one(k01), - k12 * k13 * k14 // one(k01), - k31 * k21 * k41 // one(k01), - k12 + k13 + k14 // one(k01), - k31 + k21 + k41 // one(k01), - k12 * k13 + k12 * k14 + k13 * k14 // one(k01), - k31 * k21 + k31 * k41 + k21 * k41 // one(k01), - k31 * k12 - 2 * k31 * k13 + k31 * k14 - 2 * k21 * k12 + - k21 * k13 + - k21 * k14 + - k12 * k41 + - k13 * k41 - 2 * k14 * k41 // one(k01), - ] - # Too slow with hybrid strategy :( - # push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # Biohydrogenation_io - ode = StructuralIdentifiability.@ODEmodel( - x5'(t) = - ( - k5 * k8 * x4(t) + k5 * x6(t) * x4(t) + k5 * x5(t) * x4(t) - - k6 * x5(t) * k7 - x5(t) * k7 * x4(t) - ) // ( - k8 * k6 + - k8 * x4(t) + - k6 * x6(t) + - k6 * x5(t) + - x6(t) * x4(t) + - x5(t) * x4(t) - ), - x7'(t) = (k9 * k10 * x6(t) - k9 * x6(t)^2) // k10, - x4'(t) = (-k5 * x4(t)) // (k6 + x4(t)), - x6'(t) = - ( - -k8 * k9 * k10 * x6(t) + k8 * k9 * x6(t)^2 - k9 * k10 * x6(t)^2 - - k9 * k10 * x6(t) * x5(t) + - k9 * x6(t)^3 + - k9 * x6(t)^2 * x5(t) + - k10 * x5(t) * k7 - ) // (k8 * k10 + k10 * x6(t) + k10 * x5(t)), - y1(t) = x4(t), - y2(t) = x5(t) - ) - ident_funcs = [k7, k6, k5, k10^2, k9 * k10, k8 + 1 // 2 * k10] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # SLIQR - ode = StructuralIdentifiability.@ODEmodel( - S'(t) = -b * In(t) * S(t) * Ninv - S(t) * Ninv * u(t), - In'(t) = -In(t) * g + s * Q(t) + a * L(t), - L'(t) = b * In(t) * S(t) * Ninv - a * L(t), - Q'(t) = -e * In(t) * g + In(t) * g - s * Q(t), - y(t) = In(t) * Ninv - ) - ident_funcs = [ - g + a, - s + g + a, - s, - Ninv, - b, - (e * s - s + a) // (e * s^2 * g - s^2 * g - s^2 * a + s * g * a + s * a^2), - e * s * g + s * a + g * a, - (e * s^2 + e * s * g - s^2 - s * g + g * a + a^2) // - (e * s^2 * g - s^2 * g - s^2 * a + s * g * a + s * a^2), - e * s * g * a, - 2 * e * Ninv * s * g + 2 * Ninv * s * a + 2 * Ninv * g * a, - ] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # St. - # Regression test: - # Failed before, as the degrees of Groebner basis were too large - ode = StructuralIdentifiability.@ODEmodel( - S'(t) = -e * S(t) - S(t) * d * W(t) + S(t) * r - S(t) * a * W(t) + R(t) * g, - R'(t) = e * S(t) + rR * R(t) + S(t) * a * W(t) - dr * R(t) * W(t) - R(t) * g, - W'(t) = T * Dd - W(t) * Dd, - y1(t) = S(t) + R(t), - y2(t) = T - ) - ident_funcs = [ - T, - Dd, - e - rR + dr * T + d * T + g - r + a * T, - (2 * rR * d - 2 * dr * r) // (dr - d), - (dr^2 + d^2 + 2 * d * a + a^2) // (dr * d + dr * a), - (e * dr - e * d + rR * a + dr * g - d * g - r * a) // (dr - d), - (e * dr^2 - e * dr * d + rR * dr * a + dr * d * g - dr * r * a - d^2 * g) // - (dr^2 + dr * a - d^2 - d * a), - ] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - # QY system. - # (this is a big one) - ode = StructuralIdentifiability.@ODEmodel( - P3'(t) = P4(t), - P0'(t) = P1(t), - P5'(t) = - ( - -P0(t) * beta_SI * phi * Mar * Ks * siga2 + - P0(t) * beta_SI * Mar * Ks * siga2 - - P0(t) * phi * M * Mar * Ks * beta_SA + - P0(t) * phi * M * Ks * siga2 * beta_SA + - P0(t) * M * Mar * Ks * beta_SA - P1(t) * beta_SI * phi * Mar * siga2 - - P1(t) * beta_SI * phi * Ks * siga2 + - P1(t) * beta_SI * Mar * siga2 + - P1(t) * beta_SI * Ks * siga2 - P1(t) * phi * M * Mar * beta_SA + - P1(t) * phi * M * siga2 * beta_SA - P1(t) * phi * Mar * Ks * beta_SA + - P1(t) * phi * Ks * siga2 * beta_SA + - P1(t) * M * Mar * beta_SA + - P1(t) * M * Ks * beta_SA + - P1(t) * Mar * Ks * beta_SA - beta_SI * phi * P2(t) * siga2 + - beta_SI * P2(t) * siga2 + - P3(t) * beta_SA - phi * M * Mar * P5(t) * siga2 - - phi * M * beta * siga2 - phi * P2(t) * Mar * beta_SA + - phi * P2(t) * siga2 * beta_SA + - M * P2(t) * beta_SA + - M * Mar * P5(t) * siga2 + - M * beta * siga2 + - P2(t) * Mar * beta_SA + - P2(t) * Ks * beta_SA - ) // (phi * M * siga2 - M * siga2), - P4'(t) = - ( - -siga1 * P0(t)^2 * beta_SI * phi * M * Mar * Ks^2 * siga2^2 + - siga1 * P0(t)^2 * beta_SI * M * Mar * Ks^2 * siga2^2 - - siga1 * P0(t)^2 * phi * M^2 * Mar * Ks^2 * siga2 * beta_SA + - siga1 * P0(t)^2 * phi * M^2 * Ks^2 * siga2^2 * beta_SA + - siga1 * P0(t)^2 * M^2 * Mar * Ks^2 * siga2 * beta_SA - - siga1 * P0(t) * P1(t) * beta_SI * phi * M * Mar * Ks^2 * siga2 - - 2 * siga1 * P0(t) * P1(t) * beta_SI * phi * M * Mar * Ks * siga2^2 - - siga1 * P0(t) * P1(t) * beta_SI * phi * M * Ks^2 * siga2^2 - - siga1 * P0(t) * P1(t) * beta_SI * phi * Mar * Ks^2 * siga2^2 + - siga1 * P0(t) * P1(t) * beta_SI * M * Mar * Ks^2 * siga2 + - 2 * siga1 * P0(t) * P1(t) * beta_SI * M * Mar * Ks * siga2^2 + - siga1 * P0(t) * P1(t) * beta_SI * M * Ks^2 * siga2^2 + - siga1 * P0(t) * P1(t) * beta_SI * Mar * Ks^2 * siga2^2 - - siga1 * P0(t) * P1(t) * phi * M^2 * Mar * Ks^2 * beta_SA - - 2 * siga1 * P0(t) * P1(t) * phi * M^2 * Mar * Ks * siga2 * beta_SA + - siga1 * P0(t) * P1(t) * phi * M^2 * Ks^2 * siga2 * beta_SA + - 2 * siga1 * P0(t) * P1(t) * phi * M^2 * Ks * siga2^2 * beta_SA - - 2 * siga1 * P0(t) * P1(t) * phi * M * Mar * Ks^2 * siga2 * beta_SA + - 2 * siga1 * P0(t) * P1(t) * phi * M * Ks^2 * siga2^2 * beta_SA + - siga1 * P0(t) * P1(t) * M^2 * Mar * Ks^2 * beta_SA + - 2 * siga1 * P0(t) * P1(t) * M^2 * Mar * Ks * siga2 * beta_SA + - siga1 * P0(t) * P1(t) * M^2 * Ks^2 * siga2 * beta_SA + - 2 * siga1 * P0(t) * P1(t) * M * Mar * Ks^2 * siga2 * beta_SA - - siga1 * P0(t) * beta_SI * P3(t) * phi * Mar * Ks * siga2 + - siga1 * P0(t) * beta_SI * P3(t) * Mar * Ks * siga2 - - siga1 * P0(t) * beta_SI * phi * M * P2(t) * Mar * Ks * siga2 - - siga1 * P0(t) * beta_SI * phi * M * P2(t) * Ks * siga2^2 - - siga1 * P0(t) * beta_SI * phi * P2(t) * Mar * Ks^2 * siga2 - - siga1 * P0(t) * beta_SI * phi * P2(t) * Mar * Ks * siga2^2 + - siga1 * P0(t) * beta_SI * M * P2(t) * Mar * Ks * siga2 + - siga1 * P0(t) * beta_SI * M * P2(t) * Ks * siga2^2 + - siga1 * P0(t) * beta_SI * P2(t) * Mar * Ks^2 * siga2 + - siga1 * P0(t) * beta_SI * P2(t) * Mar * Ks * siga2^2 - - siga1 * P0(t) * P3(t) * phi * M * Mar * Ks * beta_SA + - siga1 * P0(t) * P3(t) * phi * M * Ks * siga2 * beta_SA + - siga1 * P0(t) * P3(t) * M * Mar * Ks * beta_SA + - siga1 * P0(t) * P3(t) * M * Ks * siga2 * beta_SA - - siga1 * P0(t) * phi * M^2 * P2(t) * Mar * Ks * beta_SA + - siga1 * P0(t) * phi * M^2 * P2(t) * Ks * siga2 * beta_SA - - siga1 * P0(t) * phi * M^2 * Mar * P5(t) * Ks * siga2^2 - - siga1 * P0(t) * phi * M^2 * Ks * beta * siga2^2 - - siga1 * P0(t) * phi * M * P2(t) * Mar * Ks^2 * beta_SA - - 2 * siga1 * P0(t) * phi * M * P2(t) * Mar * Ks * siga2 * beta_SA + - siga1 * P0(t) * phi * M * P2(t) * Ks^2 * siga2 * beta_SA + - 2 * siga1 * P0(t) * phi * M * P2(t) * Ks * siga2^2 * beta_SA + - siga1 * P0(t) * M^2 * P2(t) * Mar * Ks * beta_SA + - siga1 * P0(t) * M^2 * P2(t) * Ks * siga2 * beta_SA + - siga1 * P0(t) * M^2 * Mar * P5(t) * Ks * siga2^2 + - siga1 * P0(t) * M^2 * Ks * beta * siga2^2 + - siga1 * P0(t) * M * P2(t) * Mar * Ks^2 * beta_SA + - 2 * siga1 * P0(t) * M * P2(t) * Mar * Ks * siga2 * beta_SA + - siga1 * P0(t) * M * P2(t) * Ks^2 * siga2 * beta_SA - - siga1 * P1(t)^2 * beta_SI * phi * M * Mar * Ks * siga2 - - siga1 * P1(t)^2 * beta_SI * phi * M * Mar * siga2^2 - - siga1 * P1(t)^2 * beta_SI * phi * M * Ks^2 * siga2 - - siga1 * P1(t)^2 * beta_SI * phi * M * Ks * siga2^2 - - siga1 * P1(t)^2 * beta_SI * phi * Mar * Ks * siga2^2 - - siga1 * P1(t)^2 * beta_SI * phi * Ks^2 * siga2^2 + - siga1 * P1(t)^2 * beta_SI * M * Mar * Ks * siga2 + - siga1 * P1(t)^2 * beta_SI * M * Mar * siga2^2 + - siga1 * P1(t)^2 * beta_SI * M * Ks^2 * siga2 + - siga1 * P1(t)^2 * beta_SI * M * Ks * siga2^2 + - siga1 * P1(t)^2 * beta_SI * Mar * Ks * siga2^2 + - siga1 * P1(t)^2 * beta_SI * Ks^2 * siga2^2 - - siga1 * P1(t)^2 * phi * M^2 * Mar * Ks * beta_SA - - siga1 * P1(t)^2 * phi * M^2 * Mar * siga2 * beta_SA + - siga1 * P1(t)^2 * phi * M^2 * Ks * siga2 * beta_SA + - siga1 * P1(t)^2 * phi * M^2 * siga2^2 * beta_SA - - siga1 * P1(t)^2 * phi * M * Mar * Ks^2 * beta_SA - - 2 * siga1 * P1(t)^2 * phi * M * Mar * Ks * siga2 * beta_SA + - siga1 * P1(t)^2 * phi * M * Ks^2 * siga2 * beta_SA + - 2 * siga1 * P1(t)^2 * phi * M * Ks * siga2^2 * beta_SA - - siga1 * P1(t)^2 * phi * Mar * Ks^2 * siga2 * beta_SA + - siga1 * P1(t)^2 * phi * Ks^2 * siga2^2 * beta_SA + - siga1 * P1(t)^2 * M^2 * Mar * Ks * beta_SA + - siga1 * P1(t)^2 * M^2 * Mar * siga2 * beta_SA + - siga1 * P1(t)^2 * M^2 * Ks^2 * beta_SA + - siga1 * P1(t)^2 * M^2 * Ks * siga2 * beta_SA + - siga1 * P1(t)^2 * M * Mar * Ks^2 * beta_SA + - 2 * siga1 * P1(t)^2 * M * Mar * Ks * siga2 * beta_SA + - siga1 * P1(t)^2 * M * Ks^2 * siga2 * beta_SA + - siga1 * P1(t)^2 * Mar * Ks^2 * siga2 * beta_SA - - siga1 * P1(t) * beta_SI * P3(t) * phi * Mar * siga2 - - siga1 * P1(t) * beta_SI * P3(t) * phi * Ks * siga2 + - siga1 * P1(t) * beta_SI * P3(t) * Mar * siga2 + - siga1 * P1(t) * beta_SI * P3(t) * Ks * siga2 - - siga1 * P1(t) * beta_SI * phi * M * P2(t) * Mar * siga2 - - 2 * siga1 * P1(t) * beta_SI * phi * M * P2(t) * Ks * siga2 - - siga1 * P1(t) * beta_SI * phi * M * P2(t) * siga2^2 - - siga1 * P1(t) * beta_SI * phi * P2(t) * Mar * Ks * siga2 - - siga1 * P1(t) * beta_SI * phi * P2(t) * Mar * siga2^2 - - siga1 * P1(t) * beta_SI * phi * P2(t) * Ks^2 * siga2 - - 2 * siga1 * P1(t) * beta_SI * phi * P2(t) * Ks * siga2^2 + - siga1 * P1(t) * beta_SI * M * P2(t) * Mar * siga2 + - 2 * siga1 * P1(t) * beta_SI * M * P2(t) * Ks * siga2 + - siga1 * P1(t) * beta_SI * M * P2(t) * siga2^2 + - siga1 * P1(t) * beta_SI * P2(t) * Mar * Ks * siga2 + - siga1 * P1(t) * beta_SI * P2(t) * Mar * siga2^2 + - siga1 * P1(t) * beta_SI * P2(t) * Ks^2 * siga2 + - 2 * siga1 * P1(t) * beta_SI * P2(t) * Ks * siga2^2 - - siga1 * P1(t) * P3(t) * phi * M * Mar * beta_SA + - siga1 * P1(t) * P3(t) * phi * M * siga2 * beta_SA - - siga1 * P1(t) * P3(t) * phi * Mar * Ks * beta_SA + - siga1 * P1(t) * P3(t) * phi * Ks * siga2 * beta_SA + - siga1 * P1(t) * P3(t) * M * Mar * beta_SA + - 2 * siga1 * P1(t) * P3(t) * M * Ks * beta_SA + - siga1 * P1(t) * P3(t) * M * siga2 * beta_SA + - siga1 * P1(t) * P3(t) * Mar * Ks * beta_SA + - siga1 * P1(t) * P3(t) * Ks * siga2 * beta_SA - - siga1 * P1(t) * phi * M^2 * P2(t) * Mar * beta_SA + - siga1 * P1(t) * phi * M^2 * P2(t) * siga2 * beta_SA - - siga1 * P1(t) * phi * M^2 * Mar * P5(t) * Ks * siga2 - - siga1 * P1(t) * phi * M^2 * Mar * P5(t) * siga2^2 - - siga1 * P1(t) * phi * M^2 * Ks * beta * siga2 - - siga1 * P1(t) * phi * M^2 * Ks * siga2^2 - - siga1 * P1(t) * phi * M^2 * beta * siga2^2 - - 3 * siga1 * P1(t) * phi * M * P2(t) * Mar * Ks * beta_SA - - 2 * siga1 * P1(t) * phi * M * P2(t) * Mar * siga2 * beta_SA + - 3 * siga1 * P1(t) * phi * M * P2(t) * Ks * siga2 * beta_SA + - 2 * siga1 * P1(t) * phi * M * P2(t) * siga2^2 * beta_SA - - siga1 * P1(t) * phi * M * Mar * P5(t) * Ks * siga2^2 - - siga1 * P1(t) * phi * M * Ks * beta * siga2^2 - - siga1 * P1(t) * phi * P2(t) * Mar * Ks^2 * beta_SA - - 2 * siga1 * P1(t) * phi * P2(t) * Mar * Ks * siga2 * beta_SA + - siga1 * P1(t) * phi * P2(t) * Ks^2 * siga2 * beta_SA + - 2 * siga1 * P1(t) * phi * P2(t) * Ks * siga2^2 * beta_SA + - siga1 * P1(t) * M^2 * P2(t) * Mar * beta_SA + - 2 * siga1 * P1(t) * M^2 * P2(t) * Ks * beta_SA + - siga1 * P1(t) * M^2 * P2(t) * siga2 * beta_SA + - siga1 * P1(t) * M^2 * Mar * P5(t) * Ks * siga2 + - siga1 * P1(t) * M^2 * Mar * P5(t) * siga2^2 + - siga1 * P1(t) * M^2 * Ks * beta * siga2 + - siga1 * P1(t) * M^2 * Ks * siga2^2 + - siga1 * P1(t) * M^2 * beta * siga2^2 + - 3 * siga1 * P1(t) * M * P2(t) * Mar * Ks * beta_SA + - 2 * siga1 * P1(t) * M * P2(t) * Mar * siga2 * beta_SA + - 2 * siga1 * P1(t) * M * P2(t) * Ks^2 * beta_SA + - 3 * siga1 * P1(t) * M * P2(t) * Ks * siga2 * beta_SA + - siga1 * P1(t) * M * Mar * P5(t) * Ks * siga2^2 + - siga1 * P1(t) * M * Ks * beta * siga2^2 + - siga1 * P1(t) * P2(t) * Mar * Ks^2 * beta_SA + - 2 * siga1 * P1(t) * P2(t) * Mar * Ks * siga2 * beta_SA + - siga1 * P1(t) * P2(t) * Ks^2 * siga2 * beta_SA - - siga1 * beta_SI * P3(t) * phi * P2(t) * siga2 + - siga1 * beta_SI * P3(t) * P2(t) * siga2 - - siga1 * beta_SI * phi * M * P2(t)^2 * siga2 - - siga1 * beta_SI * phi * P2(t)^2 * Ks * siga2 - - siga1 * beta_SI * phi * P2(t)^2 * siga2^2 + - siga1 * beta_SI * M * P2(t)^2 * siga2 + - siga1 * beta_SI * P2(t)^2 * Ks * siga2 + - siga1 * beta_SI * P2(t)^2 * siga2^2 + - siga1 * P3(t)^2 * beta_SA - siga1 * P3(t) * phi * M^2 * siga2 - - siga1 * P3(t) * phi * M * Mar * P5(t) * siga2 - - siga1 * P3(t) * phi * M * Ks * siga2 - - siga1 * P3(t) * phi * M * beta * siga2 - - siga1 * P3(t) * phi * M * siga2^2 - - siga1 * P3(t) * phi * P2(t) * Mar * beta_SA + - siga1 * P3(t) * phi * P2(t) * siga2 * beta_SA + - siga1 * P3(t) * M^2 * siga2 + - 2 * siga1 * P3(t) * M * P2(t) * beta_SA + - siga1 * P3(t) * M * Mar * P5(t) * siga2 + - siga1 * P3(t) * M * Ks * siga2 + - siga1 * P3(t) * M * beta * siga2 + - siga1 * P3(t) * M * siga2^2 + - siga1 * P3(t) * P2(t) * Mar * beta_SA + - 2 * siga1 * P3(t) * P2(t) * Ks * beta_SA + - siga1 * P3(t) * P2(t) * siga2 * beta_SA - - siga1 * phi * M^2 * P2(t) * Mar * P5(t) * siga2 - - siga1 * phi * M^2 * P2(t) * Ks * siga2 - - siga1 * phi * M^2 * P2(t) * beta * siga2 - - siga1 * phi * M^2 * P2(t) * siga2^2 - siga1 * phi * M * P4(t) * siga2 - - siga1 * phi * M * P2(t)^2 * Mar * beta_SA + - siga1 * phi * M * P2(t)^2 * siga2 * beta_SA - - siga1 * phi * M * P2(t) * Mar * P5(t) * Ks * siga2 - - siga1 * phi * M * P2(t) * Mar * P5(t) * siga2^2 - - siga1 * phi * M * P2(t) * Ks * beta * siga2 - - siga1 * phi * M * P2(t) * Ks * siga2^2 - - siga1 * phi * M * P2(t) * beta * siga2^2 - - siga1 * phi * P2(t)^2 * Mar * Ks * beta_SA - - siga1 * phi * P2(t)^2 * Mar * siga2 * beta_SA + - siga1 * phi * P2(t)^2 * Ks * siga2 * beta_SA + - siga1 * phi * P2(t)^2 * siga2^2 * beta_SA + - siga1 * M^2 * P2(t)^2 * beta_SA + - siga1 * M^2 * P2(t) * Mar * P5(t) * siga2 + - siga1 * M^2 * P2(t) * Ks * siga2 + - siga1 * M^2 * P2(t) * beta * siga2 + - siga1 * M^2 * P2(t) * siga2^2 + - siga1 * M * P4(t) * siga2 + - siga1 * M * P2(t)^2 * Mar * beta_SA + - 2 * siga1 * M * P2(t)^2 * Ks * beta_SA + - siga1 * M * P2(t)^2 * siga2 * beta_SA + - siga1 * M * P2(t) * Mar * P5(t) * Ks * siga2 + - siga1 * M * P2(t) * Mar * P5(t) * siga2^2 + - siga1 * M * P2(t) * Ks * beta * siga2 + - siga1 * M * P2(t) * Ks * siga2^2 + - siga1 * M * P2(t) * beta * siga2^2 + - siga1 * P2(t)^2 * Mar * Ks * beta_SA + - siga1 * P2(t)^2 * Mar * siga2 * beta_SA + - siga1 * P2(t)^2 * Ks^2 * beta_SA + - siga1 * P2(t)^2 * Ks * siga2 * beta_SA - - P0(t) * P1(t) * beta_SI * phi * M * Mar * Ks^2 * siga2^2 + - P0(t) * P1(t) * beta_SI * M * Mar * Ks^2 * siga2^2 - - P0(t) * P1(t) * phi * M^2 * Mar * Ks^2 * siga2 * beta_SA + - P0(t) * P1(t) * phi * M^2 * Ks^2 * siga2^2 * beta_SA + - P0(t) * P1(t) * M^2 * Mar * Ks^2 * siga2 * beta_SA - - P0(t) * beta_SI * P3(t) * phi * M * Mar * Ks * siga2 - - P0(t) * beta_SI * P3(t) * phi * Mar * Ks^2 * siga2 - - P0(t) * beta_SI * P3(t) * phi * Mar * Ks * siga2^2 + - P0(t) * beta_SI * P3(t) * M * Mar * Ks * siga2 + - P0(t) * beta_SI * P3(t) * Mar * Ks^2 * siga2 + - P0(t) * beta_SI * P3(t) * Mar * Ks * siga2^2 - - P0(t) * beta_SI * phi * alpa * Mar * Ks * siga2 - - P0(t) * beta_SI * phi * M * P2(t) * Mar * Ks^2 * siga2 - - P0(t) * beta_SI * phi * M * P2(t) * Mar * Ks * siga2^2 - - P0(t) * beta_SI * phi * P4(t) * Mar * Ks * siga2 - - P0(t) * beta_SI * phi * P2(t) * Mar * Ks^2 * siga2^2 + - P0(t) * beta_SI * alpa * Mar * Ks * siga2 + - P0(t) * beta_SI * M * P2(t) * Mar * Ks^2 * siga2 + - P0(t) * beta_SI * M * P2(t) * Mar * Ks * siga2^2 + - P0(t) * beta_SI * P4(t) * Mar * Ks * siga2 + - P0(t) * beta_SI * P2(t) * Mar * Ks^2 * siga2^2 - - P0(t) * P3(t) * phi * M^2 * Mar * Ks * beta_SA + - P0(t) * P3(t) * phi * M^2 * Ks * siga2 * beta_SA - - P0(t) * P3(t) * phi * M * Mar * Ks^2 * beta_SA - - P0(t) * P3(t) * phi * M * Mar * Ks * siga2 * beta_SA + - P0(t) * P3(t) * phi * M * Ks^2 * siga2 * beta_SA + - P0(t) * P3(t) * phi * M * Ks * siga2^2 * beta_SA + - P0(t) * P3(t) * M^2 * Mar * Ks * beta_SA + - P0(t) * P3(t) * M * Mar * Ks^2 * beta_SA + - P0(t) * P3(t) * M * Mar * Ks * siga2 * beta_SA - - P0(t) * phi * alpa * M * Mar * Ks * beta_SA + - P0(t) * phi * alpa * M * Ks * siga2 * beta_SA - - P0(t) * phi * M^2 * P2(t) * Mar * Ks^2 * beta_SA - - P0(t) * phi * M^2 * P2(t) * Mar * Ks * siga2 * beta_SA + - P0(t) * phi * M^2 * P2(t) * Ks^2 * siga2 * beta_SA + - P0(t) * phi * M^2 * P2(t) * Ks * siga2^2 * beta_SA - - P0(t) * phi * M * P4(t) * Mar * Ks * beta_SA + - P0(t) * phi * M * P4(t) * Ks * siga2 * beta_SA - - P0(t) * phi * M * P2(t) * Mar * Ks^2 * siga2 * beta_SA + - P0(t) * phi * M * P2(t) * Ks^2 * siga2^2 * beta_SA + - P0(t) * alpa * M * Mar * Ks * beta_SA + - P0(t) * M^2 * P2(t) * Mar * Ks^2 * beta_SA + - P0(t) * M^2 * P2(t) * Mar * Ks * siga2 * beta_SA + - P0(t) * M * P4(t) * Mar * Ks * beta_SA + - P0(t) * M * P2(t) * Mar * Ks^2 * siga2 * beta_SA - - P1(t)^2 * beta_SI * phi * M * Mar * Ks * siga2^2 - - P1(t)^2 * beta_SI * phi * M * Ks^2 * siga2^2 + - P1(t)^2 * beta_SI * M * Mar * Ks * siga2^2 + - P1(t)^2 * beta_SI * M * Ks^2 * siga2^2 - - P1(t)^2 * phi * M^2 * Mar * Ks * siga2 * beta_SA + - P1(t)^2 * phi * M^2 * Ks * siga2^2 * beta_SA - - P1(t)^2 * phi * M * Mar * Ks^2 * siga2 * beta_SA + - P1(t)^2 * phi * M * Ks^2 * siga2^2 * beta_SA + - P1(t)^2 * M^2 * Mar * Ks * siga2 * beta_SA + - P1(t)^2 * M^2 * Ks^2 * siga2 * beta_SA + - P1(t)^2 * M * Mar * Ks^2 * siga2 * beta_SA - - P1(t) * beta_SI * P3(t) * phi * M * Mar * siga2 - - P1(t) * beta_SI * P3(t) * phi * M * Ks * siga2 - - P1(t) * beta_SI * P3(t) * phi * Mar * Ks * siga2 - - P1(t) * beta_SI * P3(t) * phi * Mar * siga2^2 - - P1(t) * beta_SI * P3(t) * phi * Ks^2 * siga2 - - P1(t) * beta_SI * P3(t) * phi * Ks * siga2^2 + - P1(t) * beta_SI * P3(t) * M * Mar * siga2 + - P1(t) * beta_SI * P3(t) * M * Ks * siga2 + - P1(t) * beta_SI * P3(t) * Mar * Ks * siga2 + - P1(t) * beta_SI * P3(t) * Mar * siga2^2 + - P1(t) * beta_SI * P3(t) * Ks^2 * siga2 + - P1(t) * beta_SI * P3(t) * Ks * siga2^2 - - P1(t) * beta_SI * phi * alpa * Mar * siga2 - - P1(t) * beta_SI * phi * alpa * Ks * siga2 - - P1(t) * beta_SI * phi * M * P2(t) * Mar * Ks * siga2 - - P1(t) * beta_SI * phi * M * P2(t) * Mar * siga2^2 - - P1(t) * beta_SI * phi * M * P2(t) * Ks^2 * siga2 - - 2 * P1(t) * beta_SI * phi * M * P2(t) * Ks * siga2^2 - - P1(t) * beta_SI * phi * P4(t) * Mar * siga2 - - P1(t) * beta_SI * phi * P4(t) * Ks * siga2 - - P1(t) * beta_SI * phi * P2(t) * Mar * Ks * siga2^2 - - P1(t) * beta_SI * phi * P2(t) * Ks^2 * siga2^2 + - P1(t) * beta_SI * alpa * Mar * siga2 + - P1(t) * beta_SI * alpa * Ks * siga2 + - P1(t) * beta_SI * M * P2(t) * Mar * Ks * siga2 + - P1(t) * beta_SI * M * P2(t) * Mar * siga2^2 + - P1(t) * beta_SI * M * P2(t) * Ks^2 * siga2 + - 2 * P1(t) * beta_SI * M * P2(t) * Ks * siga2^2 + - P1(t) * beta_SI * P4(t) * Mar * siga2 + - P1(t) * beta_SI * P4(t) * Ks * siga2 + - P1(t) * beta_SI * P2(t) * Mar * Ks * siga2^2 + - P1(t) * beta_SI * P2(t) * Ks^2 * siga2^2 - - P1(t) * P3(t) * phi * M^2 * Mar * beta_SA + - P1(t) * P3(t) * phi * M^2 * siga2 * beta_SA - - 2 * P1(t) * P3(t) * phi * M * Mar * Ks * beta_SA - - P1(t) * P3(t) * phi * M * Mar * siga2 * beta_SA + - 2 * P1(t) * P3(t) * phi * M * Ks * siga2 * beta_SA + - P1(t) * P3(t) * phi * M * siga2^2 * beta_SA - - P1(t) * P3(t) * phi * Mar * Ks^2 * beta_SA - - P1(t) * P3(t) * phi * Mar * Ks * siga2 * beta_SA + - P1(t) * P3(t) * phi * Ks^2 * siga2 * beta_SA + - P1(t) * P3(t) * phi * Ks * siga2^2 * beta_SA + - P1(t) * P3(t) * M^2 * Mar * beta_SA + - P1(t) * P3(t) * M^2 * Ks * beta_SA + - 2 * P1(t) * P3(t) * M * Mar * Ks * beta_SA + - P1(t) * P3(t) * M * Mar * siga2 * beta_SA + - P1(t) * P3(t) * M * Ks^2 * beta_SA + - 2 * P1(t) * P3(t) * M * Ks * siga2 * beta_SA + - P1(t) * P3(t) * Mar * Ks^2 * beta_SA + - P1(t) * P3(t) * Mar * Ks * siga2 * beta_SA - - P1(t) * phi * alpa * M * Mar * beta_SA + - P1(t) * phi * alpa * M * siga2 * beta_SA - - P1(t) * phi * alpa * Mar * Ks * beta_SA + - P1(t) * phi * alpa * Ks * siga2 * beta_SA - - P1(t) * phi * M^2 * P2(t) * Mar * Ks * beta_SA - - P1(t) * phi * M^2 * P2(t) * Mar * siga2 * beta_SA + - P1(t) * phi * M^2 * P2(t) * Ks * siga2 * beta_SA + - P1(t) * phi * M^2 * P2(t) * siga2^2 * beta_SA - - P1(t) * phi * M^2 * Mar * P5(t) * Ks * siga2^2 - - P1(t) * phi * M^2 * Ks * beta * siga2^2 - - P1(t) * phi * M * P4(t) * Mar * beta_SA + - P1(t) * phi * M * P4(t) * siga2 * beta_SA - - P1(t) * phi * M * P2(t) * Mar * Ks^2 * beta_SA - - 3 * P1(t) * phi * M * P2(t) * Mar * Ks * siga2 * beta_SA + - P1(t) * phi * M * P2(t) * Ks^2 * siga2 * beta_SA + - 3 * P1(t) * phi * M * P2(t) * Ks * siga2^2 * beta_SA - - P1(t) * phi * P4(t) * Mar * Ks * beta_SA + - P1(t) * phi * P4(t) * Ks * siga2 * beta_SA - - P1(t) * phi * P2(t) * Mar * Ks^2 * siga2 * beta_SA + - P1(t) * phi * P2(t) * Ks^2 * siga2^2 * beta_SA + - P1(t) * alpa * M * Mar * beta_SA + - P1(t) * alpa * M * Ks * beta_SA + - P1(t) * alpa * Mar * Ks * beta_SA + - P1(t) * M^2 * P2(t) * Mar * Ks * beta_SA + - P1(t) * M^2 * P2(t) * Mar * siga2 * beta_SA + - P1(t) * M^2 * P2(t) * Ks^2 * beta_SA + - 2 * P1(t) * M^2 * P2(t) * Ks * siga2 * beta_SA + - P1(t) * M^2 * Mar * P5(t) * Ks * siga2^2 + - P1(t) * M^2 * Ks * beta * siga2^2 + - P1(t) * M * P4(t) * Mar * beta_SA + - P1(t) * M * P4(t) * Ks * beta_SA + - P1(t) * M * P2(t) * Mar * Ks^2 * beta_SA + - 3 * P1(t) * M * P2(t) * Mar * Ks * siga2 * beta_SA + - 2 * P1(t) * M * P2(t) * Ks^2 * siga2 * beta_SA + - P1(t) * P4(t) * Mar * Ks * beta_SA + - P1(t) * P2(t) * Mar * Ks^2 * siga2 * beta_SA - - beta_SI * P3(t) * phi * M * P2(t) * siga2 - - beta_SI * P3(t) * phi * P2(t) * Ks * siga2 - - beta_SI * P3(t) * phi * P2(t) * siga2^2 + - beta_SI * P3(t) * M * P2(t) * siga2 + - beta_SI * P3(t) * P2(t) * Ks * siga2 + - beta_SI * P3(t) * P2(t) * siga2^2 - - beta_SI * phi * alpa * P2(t) * siga2 - - beta_SI * phi * M * P2(t)^2 * Ks * siga2 - - beta_SI * phi * M * P2(t)^2 * siga2^2 - - beta_SI * phi * P4(t) * P2(t) * siga2 - - beta_SI * phi * P2(t)^2 * Ks * siga2^2 + - beta_SI * alpa * P2(t) * siga2 + - beta_SI * M * P2(t)^2 * Ks * siga2 + - beta_SI * M * P2(t)^2 * siga2^2 + - beta_SI * P4(t) * P2(t) * siga2 + - beta_SI * P2(t)^2 * Ks * siga2^2 + - P3(t)^2 * M * beta_SA + - P3(t)^2 * Ks * beta_SA + - P3(t)^2 * siga2 * beta_SA - P3(t) * phi * M^2 * Mar * P5(t) * siga2 - - P3(t) * phi * M^2 * Ks * siga2 - P3(t) * phi * M^2 * beta * siga2 - - P3(t) * phi * M^2 * siga2^2 - P3(t) * phi * M * P2(t) * Mar * beta_SA + - P3(t) * phi * M * P2(t) * siga2 * beta_SA - - P3(t) * phi * M * Mar * P5(t) * Ks * siga2 - - P3(t) * phi * M * Mar * P5(t) * siga2^2 - - P3(t) * phi * M * Ks * beta * siga2 - P3(t) * phi * M * Ks * siga2^2 - - P3(t) * phi * M * beta * siga2^2 - - P3(t) * phi * P2(t) * Mar * Ks * beta_SA - - P3(t) * phi * P2(t) * Mar * siga2 * beta_SA + - P3(t) * phi * P2(t) * Ks * siga2 * beta_SA + - P3(t) * phi * P2(t) * siga2^2 * beta_SA + - P3(t) * alpa * beta_SA + - P3(t) * M^2 * P2(t) * beta_SA + - P3(t) * M^2 * Mar * P5(t) * siga2 + - P3(t) * M^2 * Ks * siga2 + - P3(t) * M^2 * beta * siga2 + - P3(t) * M^2 * siga2^2 + - P3(t) * M * P2(t) * Mar * beta_SA + - 3 * P3(t) * M * P2(t) * Ks * beta_SA + - 2 * P3(t) * M * P2(t) * siga2 * beta_SA + - P3(t) * M * Mar * P5(t) * Ks * siga2 + - P3(t) * M * Mar * P5(t) * siga2^2 + - P3(t) * M * Ks * beta * siga2 + - P3(t) * M * Ks * siga2^2 + - P3(t) * M * beta * siga2^2 + - P3(t) * P4(t) * beta_SA + - P3(t) * P2(t) * Mar * Ks * beta_SA + - P3(t) * P2(t) * Mar * siga2 * beta_SA + - P3(t) * P2(t) * Ks^2 * beta_SA + - 2 * P3(t) * P2(t) * Ks * siga2 * beta_SA - - phi * alpa * M * Mar * P5(t) * siga2 - phi * alpa * M * beta * siga2 - - phi * alpa * P2(t) * Mar * beta_SA + - phi * alpa * P2(t) * siga2 * beta_SA - phi * M^2 * P4(t) * siga2 - - phi * M^2 * P2(t) * Mar * P5(t) * Ks * siga2 - - phi * M^2 * P2(t) * Mar * P5(t) * siga2^2 - - phi * M^2 * P2(t) * Ks * beta * siga2 - - phi * M^2 * P2(t) * Ks * siga2^2 - phi * M^2 * P2(t) * beta * siga2^2 - - phi * M * P4(t) * Mar * P5(t) * siga2 - phi * M * P4(t) * Ks * siga2 - - phi * M * P4(t) * beta * siga2 - phi * M * P4(t) * siga2^2 - - phi * M * P2(t)^2 * Mar * Ks * beta_SA - - phi * M * P2(t)^2 * Mar * siga2 * beta_SA + - phi * M * P2(t)^2 * Ks * siga2 * beta_SA + - phi * M * P2(t)^2 * siga2^2 * beta_SA - - phi * M * P2(t) * Mar * P5(t) * Ks * siga2^2 - - phi * M * P2(t) * Ks * beta * siga2^2 - - phi * P4(t) * P2(t) * Mar * beta_SA + - phi * P4(t) * P2(t) * siga2 * beta_SA - - phi * P2(t)^2 * Mar * Ks * siga2 * beta_SA + - phi * P2(t)^2 * Ks * siga2^2 * beta_SA + - alpa * M * P2(t) * beta_SA + - alpa * M * Mar * P5(t) * siga2 + - alpa * M * beta * siga2 + - alpa * P2(t) * Mar * beta_SA + - alpa * P2(t) * Ks * beta_SA + - M^2 * P4(t) * siga2 + - M^2 * P2(t)^2 * Ks * beta_SA + - M^2 * P2(t)^2 * siga2 * beta_SA + - M^2 * P2(t) * Mar * P5(t) * Ks * siga2 + - M^2 * P2(t) * Mar * P5(t) * siga2^2 + - M^2 * P2(t) * Ks * beta * siga2 + - M^2 * P2(t) * Ks * siga2^2 + - M^2 * P2(t) * beta * siga2^2 + - M * P4(t) * P2(t) * beta_SA + - M * P4(t) * Mar * P5(t) * siga2 + - M * P4(t) * Ks * siga2 + - M * P4(t) * beta * siga2 + - M * P4(t) * siga2^2 + - M * P2(t)^2 * Mar * Ks * beta_SA + - M * P2(t)^2 * Mar * siga2 * beta_SA + - M * P2(t)^2 * Ks^2 * beta_SA + - 2 * M * P2(t)^2 * Ks * siga2 * beta_SA + - M * P2(t) * Mar * P5(t) * Ks * siga2^2 + - M * P2(t) * Ks * beta * siga2^2 + - P4(t) * P2(t) * Mar * beta_SA + - P4(t) * P2(t) * Ks * beta_SA + - P2(t)^2 * Mar * Ks * siga2 * beta_SA + - P2(t)^2 * Ks^2 * siga2 * beta_SA - ) // (phi * M * siga2 - M * siga2), - P1'(t) = P2(t), - P2'(t) = P3(t), - y(t) = P0(t) - ) - ident_funcs = [ - Mar, - Ks, - alpa, - (siga1 + phi * Mar - Mar) // phi, - (siga1 * phi - siga1 - phi * Mar + Mar) // (siga1 * phi * beta_SA), +# For each ODE system we check the equality (in terms of fields of rational +# functions) of the true set of identifiable functions and the obtained +# simplified set +test_cases = [] + +### + +ode = StructuralIdentifiability.@ODEmodel(x'(t) = a * x(t) + u(t), y(t) = x(t)) +ident_funcs = [a] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +ode = StructuralIdentifiability.@ODEmodel(x1'(t) = a, x2'(t) = -a, y(t) = x1 + x2) +ident_funcs = [] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Parameter a is not identifiable, and neither are any combinations thereof. +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = x2(t) - a, + x2'(t) = x1(t) + a, + y(t) = x1(t) + x2(t) +) +ident_funcs = + Vector{StructuralIdentifiability.AbstractAlgebra.Generic.Frac{typeof(x1)}}() + +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Example 2 from +# "On Global Identifiability for Arbitrary Model Parametrizations", +# DOI: 10.1016/0005-1098(94)90029-9 +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = Θ * x2(t)^2, + x2'(t) = u(t), + y(t) = x1(t) +) +ident_funcs = [Θ] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Example 4 from +# "On Global Identifiability for Arbitrary Model Parametrizations", +# DOI: 10.1016/0005-1098(94)90029-9 +ode = StructuralIdentifiability.@ODEmodel( + x'(t) = (-V_m * x(t)) / (k_m + x(t)) + k01 * x(t), + y(t) = c * x(t) +) +ident_funcs = [k01, k_m // V_m, V_m * c] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Parameters b and c enter the io equations only as the product b * c. +ode = StructuralIdentifiability.@ODEmodel(x'(t) = a * x(t) + b * u(t), y(t) = c * x(t)) +ident_funcs = [b * c, a] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# 2-compartiment model +ode = StructuralIdentifiability.@ODEmodel( + x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t), + x1'(t) = a21 * x0(t) - a12 * x1(t), + y(t) = x0(t) +) +ident_funcs = [(a01 * a12), (a01 + a12 + a21)] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# TODO: uncomment when identifiability can handle models with no states +# ode = StructuralIdentifiability.@ODEmodel( +# y(t) = a*u(t) +# ) +# ident_funcs = [(a01 * a12), (a01 + a12 + a21)] +# push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Example 2 from +# "On Structural Identifiability", +# DOI: https://doi.org/10.1016/0025-5564(70)90132-X +# +# More or less the same 2-compartmental model as the one given above +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = -(k1 + k2) * x1(t) + k3 * x2(t) + u(t), + x2'(t) = k2 * x1(t) - (k3 + k4) * x2(t), + y(t) = x1(t) +) +ident_funcs = [(k1 + k2), (k1 + k2 + k3 + k4), ((k1 + k2) * (k3 + k4) - k2 * k3)] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Diagonal with simple spectrum and observable states +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = λ1 * x1(t) + β1 * u1(t), + x2'(t) = λ2 * x2(t) + β2 * u2(t), + x3'(t) = λ3 * x3(t) + β3 * u3(t), + y(t) = x1(t) + x2(t) + x3(t) +) +ident_funcs = [λ1, λ2, λ3, β1, β2, β3] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# 3 compartments: +# x1 <--> x2 <--> x3 +# If we observe x1 and control x1, then all parameters are identifiable +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = -a1 * x1(t) + b1 * x2(t) + u(t), + x2'(t) = -(a2 + b1) * x2(t) + a1 * x1(t) + b2 * x3(t), + x3'(t) = -b2 * x3(t) + a2 * x2(t), + y(t) = x1(t) +) +ident_funcs = [a1, a2, b1, b2] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Example 3 from +# "On Structural Identifiability", +# DOI: https://doi.org/10.1016/0025-5564(70)90132-X +# +# 3 compartments: +# x1 <--> x2 <--> x3 +# If we observe x1 and control x3, then only some functions of parameters +# are identifiable +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = -a1 * x1(t) + b1 * x2(t), + x2'(t) = -(a2 + b1) * x2(t) + a1 * x1(t) + b2 * x3(t), + x3'(t) = -b2 * x3(t) + a2 * x2(t) + u(t), + y(t) = x1(t) +) +ident_funcs = [b1 * b2, a1 + a2 + b1 + b2, a1 * a2 + a1 * b2] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Example 3 from +# "On the identifiability and distinguishability of nonlinear parametric +# models", +# DOI: https://doi.org/10.1016/0378-4754(95)00123-9 +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = p1 * x1^2 + p2 * x1 * x2, + x2'(t) = p3 * x1^2 + p4 * x1 * x2, + y(t) = x1 +) +ident_funcs = [p1 + p4, p2 * p3 - p4 * p1] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Goowdin oscillator +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = -b * x1(t) + 1 / (c + x4(t)), + x2'(t) = alpha * x1(t) - beta * x2(t), + x3'(t) = gama * x2(t) - delta * x3(t), + x4'(t) = sigma * x4(t) * (gama * x2(t) - delta * x3(t)) / x3(t), + y(t) = x1(t) +) +ident_funcs = [sigma, delta + beta, c, b, delta * beta] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# SIRS forced +ode = StructuralIdentifiability.@ODEmodel( + s'(t) = mu - mu * s(t) - b0 * (1 + b1 * x1(t)) * i(t) * s(t) + g * r(t), + i'(t) = b0 * (1 + b1 * x1(t)) * i(t) * s(t) - (nu + mu) * i(t), + r'(t) = nu * i(t) - (mu + g) * r(t), + x1'(t) = -M * x2(t), + x2'(t) = M * x1(t), + y1(t) = i(t), + y2(t) = r(t) +) +ident_funcs = [g, mu, b0, nu, M^2] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# SEIR_1_io +ode = StructuralIdentifiability.@ODEmodel( + S'(t) = -beta * S(t) * I(t), + E'(t) = beta * S(t) * I(t) - v * E(t), + I'(t) = v * E(t) - psi * I(t) - (1 - psi) * gamma * I(t), + R'(t) = gamma * Q(t) + (1 - psi) * gamma * I(t), + Q'(t) = -gamma * Q(t) + psi * I(t), + y1(t) = Q(t) +) +ident_funcs = [gamma, beta // psi, gamma * psi - v - psi, gamma * psi - v * psi] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Bilirubin2_io. +# Regression test: failed before, as the total degrees were being estimated +# incorrectly in the interpolation +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = + -(k21 + k31 + k41 + k01) * x1(t) + + k12 * x2(t) + + k13 * x3(t) + + k14 * x4(t) + + u(t), + x2'(t) = k21 * x1(t) - k12 * x2(t), + x3'(t) = k31 * x1(t) - k13 * x3(t), + x4'(t) = k41 * x1(t) - k14 * x4(t), + y1(t) = x1(t) +) +ident_funcs = [ + k01 // one(k01), + k12 * k13 * k14 // one(k01), + k31 * k21 * k41 // one(k01), + k12 + k13 + k14 // one(k01), + k31 + k21 + k41 // one(k01), + k12 * k13 + k12 * k14 + k13 * k14 // one(k01), + k31 * k21 + k31 * k41 + k21 * k41 // one(k01), + k31 * k12 - 2 * k31 * k13 + k31 * k14 - 2 * k21 * k12 + + k21 * k13 + + k21 * k14 + + k12 * k41 + + k13 * k41 - 2 * k14 * k41 // one(k01), +] +# Too slow with hybrid strategy :( +# push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# Biohydrogenation_io +ode = StructuralIdentifiability.@ODEmodel( + x5'(t) = ( - siga1 * beta_SA + beta_SI * phi * siga2 + phi * Mar * beta_SA - - phi * siga2 * beta_SA - Mar * beta_SA - ) // (phi * M * siga2), + k5 * k8 * x4(t) + k5 * x6(t) * x4(t) + k5 * x5(t) * x4(t) - + k6 * x5(t) * k7 - x5(t) * k7 * x4(t) + ) // ( + k8 * k6 + + k8 * x4(t) + + k6 * x6(t) + + k6 * x5(t) + + x6(t) * x4(t) + + x5(t) * x4(t) + ), + x7'(t) = (k9 * k10 * x6(t) - k9 * x6(t)^2) // k10, + x4'(t) = (-k5 * x4(t)) // (k6 + x4(t)), + x6'(t) = ( - siga1 * phi * M * siga2 - siga1 * M * siga2 - phi * M * Mar * siga2 + - M * Mar * siga2 - ) // (siga1 * beta_SA + phi * Mar * beta_SA - Mar * beta_SA), + -k8 * k9 * k10 * x6(t) + k8 * k9 * x6(t)^2 - k9 * k10 * x6(t)^2 - + k9 * k10 * x6(t) * x5(t) + + k9 * x6(t)^3 + + k9 * x6(t)^2 * x5(t) + + k10 * x5(t) * k7 + ) // (k8 * k10 + k10 * x6(t) + k10 * x5(t)), + y1(t) = x4(t), + y2(t) = x5(t) +) +ident_funcs = [k7, k6, k5, k10^2, k9 * k10, k8 + 1 // 2 * k10] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# SLIQR +ode = StructuralIdentifiability.@ODEmodel( + S'(t) = -b * In(t) * S(t) * Ninv - S(t) * Ninv * u(t), + In'(t) = -In(t) * g + s * Q(t) + a * L(t), + L'(t) = b * In(t) * S(t) * Ninv - a * L(t), + Q'(t) = -e * In(t) * g + In(t) * g - s * Q(t), + y(t) = In(t) * Ninv +) +ident_funcs = [ + g + a, + s + g + a, + s, + Ninv, + b, + (e * s - s + a) // (e * s^2 * g - s^2 * g - s^2 * a + s * g * a + s * a^2), + e * s * g + s * a + g * a, + (e * s^2 + e * s * g - s^2 - s * g + g * a + a^2) // + (e * s^2 * g - s^2 * g - s^2 * a + s * g * a + s * a^2), + e * s * g * a, + 2 * e * Ninv * s * g + 2 * Ninv * s * a + 2 * Ninv * g * a, +] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# St. +# Regression test: +# Failed before, as the degrees of Groebner basis were too large +ode = StructuralIdentifiability.@ODEmodel( + S'(t) = -e * S(t) - S(t) * d * W(t) + S(t) * r - S(t) * a * W(t) + R(t) * g, + R'(t) = e * S(t) + rR * R(t) + S(t) * a * W(t) - dr * R(t) * W(t) - R(t) * g, + W'(t) = T * Dd - W(t) * Dd, + y1(t) = S(t) + R(t), + y2(t) = T +) +ident_funcs = [ + T, + Dd, + e - rR + dr * T + d * T + g - r + a * T, + (2 * rR * d - 2 * dr * r) // (dr - d), + (dr^2 + d^2 + 2 * d * a + a^2) // (dr * d + dr * a), + (e * dr - e * d + rR * a + dr * g - d * g - r * a) // (dr - d), + (e * dr^2 - e * dr * d + rR * dr * a + dr * d * g - dr * r * a - d^2 * g) // + (dr^2 + dr * a - d^2 - d * a), +] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +# QY system. +# (this is a big one) +ode = StructuralIdentifiability.@ODEmodel( + P3'(t) = P4(t), + P0'(t) = P1(t), + P5'(t) = ( - siga1 * beta_SI * phi * siga2 - siga1 * beta_SI * siga2 - - siga1 * phi * siga2 * beta_SA - siga1 * M * beta_SA - - beta_SI * phi * Mar * siga2 + beta_SI * Mar * siga2 - - phi * M * Mar * beta_SA + M * Mar * beta_SA - ) // (siga1 * beta_SA + phi * Mar * beta_SA - Mar * beta_SA), + -P0(t) * beta_SI * phi * Mar * Ks * siga2 + + P0(t) * beta_SI * Mar * Ks * siga2 - + P0(t) * phi * M * Mar * Ks * beta_SA + + P0(t) * phi * M * Ks * siga2 * beta_SA + + P0(t) * M * Mar * Ks * beta_SA - P1(t) * beta_SI * phi * Mar * siga2 - + P1(t) * beta_SI * phi * Ks * siga2 + + P1(t) * beta_SI * Mar * siga2 + + P1(t) * beta_SI * Ks * siga2 - P1(t) * phi * M * Mar * beta_SA + + P1(t) * phi * M * siga2 * beta_SA - P1(t) * phi * Mar * Ks * beta_SA + + P1(t) * phi * Ks * siga2 * beta_SA + + P1(t) * M * Mar * beta_SA + + P1(t) * M * Ks * beta_SA + + P1(t) * Mar * Ks * beta_SA - beta_SI * phi * P2(t) * siga2 + + beta_SI * P2(t) * siga2 + + P3(t) * beta_SA - phi * M * Mar * P5(t) * siga2 - + phi * M * beta * siga2 - phi * P2(t) * Mar * beta_SA + + phi * P2(t) * siga2 * beta_SA + + M * P2(t) * beta_SA + + M * Mar * P5(t) * siga2 + + M * beta * siga2 + + P2(t) * Mar * beta_SA + + P2(t) * Ks * beta_SA + ) // (phi * M * siga2 - M * siga2), + P4'(t) = ( - siga1^2 * beta_SA + siga1 * beta_SI * phi * siga2 - siga1 * beta_SI * siga2 + siga1 * phi * Mar * beta_SA - - siga1 * phi * siga2 * beta_SA - siga1 * Mar * beta_SA + - siga1 * siga2 * beta_SA + - beta_SI * phi * M * siga2 - beta_SI * phi * Mar * siga2 + - beta_SI * phi * siga2^2 + - beta_SI * Mar * siga2 + - phi * Mar * siga2 * beta_SA - phi * siga2^2 * beta_SA - - Mar * siga2 * beta_SA - ) // (phi * M * siga2), - ] - # Really large and takes a lot of time, so commented - # push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - ### - # Cases with states - - ode = StructuralIdentifiability.@ODEmodel(x'(t) = x(t), y(t) = x(t)) - T = typeof(x) - ident_funcs = [x] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs, with_states = true)) - - ode = StructuralIdentifiability.@ODEmodel(x'(t) = a * x(t) + u(t), y(t) = x(t)) - ident_funcs = [a, x] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = a * x1(t) - b * x1(t) * x2(t), - x2'(t) = -c * x2(t) + d * x1(t) * x2(t), - y(t) = x1(t) - ) - ident_funcs = [x1, c, d, a, b * x2] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = a * x1(t) - b * x1(t) * x2(t), - x2'(t) = -c * x2(t) + d * x1(t) * x2(t), - y(t) = x1(t) - ) - ident_funcs = [x1, c, d, a, b * x2] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - # Diagonal with simple spectrum and observable states - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = λ1 * x1(t) + β1 * u1(t), - x2'(t) = λ2 * x2(t) + β2 * u2(t), - x3'(t) = λ3 * x3(t) + β3 * u3(t), - y(t) = x1(t) + x2(t) + x3(t) - ) - ident_funcs = [λ1, λ2, λ3, β1, β2, β3, x1, x2, x3] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = x1(t) + Θ * x2(t), - x2'(t) = 0, - y(t) = x1(t) - ) - ident_funcs = [x1, x2 * Θ] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = α * x2(t), - x2'(t) = x3(t), - x3'(t) = C, - y(t) = x1(t) - ) - ident_funcs = [x1, α * x2, α * x3, α * C] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = α * (x1 - x2), - x2'(t) = α * (x1 + x2), - y(t) = (x1^2 + x2^2) // 2, - ) - ident_funcs = [α, x1^2 + x2^2] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - ode = StructuralIdentifiability.@ODEmodel(x'(t) = a * x(t) + b * u(t), y(t) = c * x(t)) - ident_funcs = [b * c, a, x // b] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - # llw1987 model - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = -p1 * x1(t) + p2 * u(t), - x2'(t) = -p3 * x2(t) + p4 * u(t), - x3'(t) = -(p1 + p3) * x3(t) + (p4 * x1(t) + p2 * x2(t)) * u(t), - y1(t) = x3(t) - ) - ident_funcs = [ - x3, - x2 * x1 // one(x1), - p3 * p1 // one(x1), - p2 * p4 // one(x1), - (p3 + p1) // one(x1), - (p2 * x2 + p4 * x1) // one(x1), - (p3 - p1) // (p2 * x2 - p4 * x1), - ] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - # Regression test: Previously failed for with_states=true because of the bug in - # `linear_relations_between_normal_forms` - # Fujita - ode = StructuralIdentifiability.@ODEmodel( - EGFR'(t) = - EGFR_turnover * pro_EGFR(t) + EGF_EGFR(t) * reaction_1_k2 - - EGFR(t) * EGFR_turnover - EGF_EGFR(t) * reaction_1_k1, - pEGFR'(t) = - EGF_EGFR(t) * reaction_9_k1 - pEGFR(t) * reaction_4_k1 + - pEGFR_Akt(t) * reaction_2_k2 + - pEGFR_Akt(t) * reaction_3_k1 - Akt(t) * pEGFR(t) * reaction_2_k1, - pEGFR_Akt'(t) = - Akt(t) * pEGFR(t) * reaction_2_k1 - pEGFR_Akt(t) * reaction_3_k1 - - pEGFR_Akt(t) * reaction_2_k2, - Akt'(t) = - pAkt(t) * reaction_7_k1 + pEGFR_Akt(t) * reaction_2_k2 - - Akt(t) * pEGFR(t) * reaction_2_k1, - pAkt'(t) = - pAkt_S6(t) * reaction_5_k2 - pAkt(t) * reaction_7_k1 + - pAkt_S6(t) * reaction_6_k1 + - pEGFR_Akt(t) * reaction_3_k1 - S6(t) * pAkt(t) * reaction_5_k1, - S6'(t) = - pAkt_S6(t) * reaction_5_k2 + pS6(t) * reaction_8_k1 - - S6(t) * pAkt(t) * reaction_5_k1, - pAkt_S6'(t) = - S6(t) * pAkt(t) * reaction_5_k1 - pAkt_S6(t) * reaction_6_k1 - - pAkt_S6(t) * reaction_5_k2, - pS6'(t) = pAkt_S6(t) * reaction_6_k1 - pS6(t) * reaction_8_k1, - EGF_EGFR'(t) = - EGF_EGFR(t) * reaction_1_k1 - EGF_EGFR(t) * reaction_9_k1 - - EGF_EGFR(t) * reaction_1_k2, - y1(t) = a1 * (pEGFR(t) + pEGFR_Akt(t)), - y2(t) = a2 * (pAkt(t) + pAkt_S6(t)), - y3(t) = a3 * pS6(t) - ) - ident_funcs = [ - reaction_8_k1, - reaction_2_k2, - reaction_5_k2, - reaction_3_k1, - reaction_6_k1, - reaction_7_k1, - reaction_4_k1, - reaction_2_k1 * pAkt_S6, - reaction_2_k1 * S6, - reaction_5_k1 * pAkt_S6, - pEGFR_Akt * reaction_2_k1, - Akt * reaction_2_k1, - a1 * pAkt_S6, - pEGFR * reaction_2_k1, - pAkt * reaction_2_k1, - a3 * pAkt_S6, - pS6 * reaction_2_k1, - a2 * pAkt_S6, - reaction_9_k1 * reaction_2_k1 * EGF_EGFR, - reaction_1_k1 - reaction_9_k1 - reaction_1_k2, - ] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - # Bruno2016 model - ode = StructuralIdentifiability.@ODEmodel( - beta'(t) = -kbeta * beta(t), - cry'(t) = -cry(t) * kcrybeta - cry(t) * kcryOH, - zea'(t) = -zea(t) * kzea, - beta10'(t) = cry(t) * kcryOH - beta10(t) * kbeta10 + kbeta * beta(t), - OHbeta10'(t) = cry(t) * kcrybeta + zea(t) * kzea - OHbeta10(t) * kOHbeta10, - betaio'(t) = cry(t) * kcrybeta + beta10(t) * kbeta10 + kbeta * beta(t), - OHbetaio'(t) = cry(t) * kcryOH + zea(t) * kzea + OHbeta10(t) * kOHbeta10, - y1(t) = beta(t), - y2(t) = beta10(t) - ) - ident_funcs = [beta10, beta, kbeta, kbeta10, cry * kcryOH, kcrybeta + kcryOH] - push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) - - # STAT-5 model from - # MODELING THE NONLINEAR DYNAMICS OF CELLULAR SIGNAL TRANSDUCTION - # DOI: https://doi.org/10.1142/S0218127404010461 - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = -k1 * x1 * EpoR_A, - x2'(t) = k1 * x1 * EpoR_A - k2 * x2^2, - x3'(t) = -k3 * x3 + 0.5 * k2 * x2^2, - x4'(t) = k3 * x3, - y1(t) = k5 * (x2 + 2x3), - y2(t) = k6 * (x1 + x2 + 2x3), - y3(t) = k7 * EpoR_A - ) - ident_funcs = [k3, k1 // k7, k5 // k2, k6 // k2, k7 * EpoR_A] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) - - ode = @ODEmodel(x1'(t) = x1, x2'(t) = x2, y(t) = x1 + x2(t)) - ident_funcs = [x1 + x2] - push!(test_cases, (ode = ode, ident_funcs = ident_funcs, with_states = true)) - - # TODO: verify that Maple returns the same - @testset "Identifiable functions of parameters" begin - p = 0.99 - for case in test_cases - for simplify in [:weak, :standard] # :strong] - ode = case.ode - true_ident_funcs = case.ident_funcs - with_states = false - if haskey(case, :with_states) - with_states = case.with_states - end - result_funcs = StructuralIdentifiability.find_identifiable_functions( - ode, - simplify = simplify, - with_states = with_states, - ) - - if isempty(true_ident_funcs) - @test isempty(result_funcs) - continue - end - - @test parent(numerator(result_funcs[1])) == parent(ode) - - R = parent(numerator(result_funcs[1])) - - @info "Test, result_funcs = \n$result_funcs" case simplify R with_states - - true_ident_funcs = map(f -> f // one(f), true_ident_funcs) - true_ident_funcs = map( - f -> StructuralIdentifiability.parent_ring_change(f, R), - true_ident_funcs, - ) - - # Check inclusion in - @test StructuralIdentifiability.fields_equal( - StructuralIdentifiability.RationalFunctionField(result_funcs), - StructuralIdentifiability.RationalFunctionField(true_ident_funcs), - p, - ) + -siga1 * P0(t)^2 * beta_SI * phi * M * Mar * Ks^2 * siga2^2 + + siga1 * P0(t)^2 * beta_SI * M * Mar * Ks^2 * siga2^2 - + siga1 * P0(t)^2 * phi * M^2 * Mar * Ks^2 * siga2 * beta_SA + + siga1 * P0(t)^2 * phi * M^2 * Ks^2 * siga2^2 * beta_SA + + siga1 * P0(t)^2 * M^2 * Mar * Ks^2 * siga2 * beta_SA - + siga1 * P0(t) * P1(t) * beta_SI * phi * M * Mar * Ks^2 * siga2 - + 2 * siga1 * P0(t) * P1(t) * beta_SI * phi * M * Mar * Ks * siga2^2 - + siga1 * P0(t) * P1(t) * beta_SI * phi * M * Ks^2 * siga2^2 - + siga1 * P0(t) * P1(t) * beta_SI * phi * Mar * Ks^2 * siga2^2 + + siga1 * P0(t) * P1(t) * beta_SI * M * Mar * Ks^2 * siga2 + + 2 * siga1 * P0(t) * P1(t) * beta_SI * M * Mar * Ks * siga2^2 + + siga1 * P0(t) * P1(t) * beta_SI * M * Ks^2 * siga2^2 + + siga1 * P0(t) * P1(t) * beta_SI * Mar * Ks^2 * siga2^2 - + siga1 * P0(t) * P1(t) * phi * M^2 * Mar * Ks^2 * beta_SA - + 2 * siga1 * P0(t) * P1(t) * phi * M^2 * Mar * Ks * siga2 * beta_SA + + siga1 * P0(t) * P1(t) * phi * M^2 * Ks^2 * siga2 * beta_SA + + 2 * siga1 * P0(t) * P1(t) * phi * M^2 * Ks * siga2^2 * beta_SA - + 2 * siga1 * P0(t) * P1(t) * phi * M * Mar * Ks^2 * siga2 * beta_SA + + 2 * siga1 * P0(t) * P1(t) * phi * M * Ks^2 * siga2^2 * beta_SA + + siga1 * P0(t) * P1(t) * M^2 * Mar * Ks^2 * beta_SA + + 2 * siga1 * P0(t) * P1(t) * M^2 * Mar * Ks * siga2 * beta_SA + + siga1 * P0(t) * P1(t) * M^2 * Ks^2 * siga2 * beta_SA + + 2 * siga1 * P0(t) * P1(t) * M * Mar * Ks^2 * siga2 * beta_SA - + siga1 * P0(t) * beta_SI * P3(t) * phi * Mar * Ks * siga2 + + siga1 * P0(t) * beta_SI * P3(t) * Mar * Ks * siga2 - + siga1 * P0(t) * beta_SI * phi * M * P2(t) * Mar * Ks * siga2 - + siga1 * P0(t) * beta_SI * phi * M * P2(t) * Ks * siga2^2 - + siga1 * P0(t) * beta_SI * phi * P2(t) * Mar * Ks^2 * siga2 - + siga1 * P0(t) * beta_SI * phi * P2(t) * Mar * Ks * siga2^2 + + siga1 * P0(t) * beta_SI * M * P2(t) * Mar * Ks * siga2 + + siga1 * P0(t) * beta_SI * M * P2(t) * Ks * siga2^2 + + siga1 * P0(t) * beta_SI * P2(t) * Mar * Ks^2 * siga2 + + siga1 * P0(t) * beta_SI * P2(t) * Mar * Ks * siga2^2 - + siga1 * P0(t) * P3(t) * phi * M * Mar * Ks * beta_SA + + siga1 * P0(t) * P3(t) * phi * M * Ks * siga2 * beta_SA + + siga1 * P0(t) * P3(t) * M * Mar * Ks * beta_SA + + siga1 * P0(t) * P3(t) * M * Ks * siga2 * beta_SA - + siga1 * P0(t) * phi * M^2 * P2(t) * Mar * Ks * beta_SA + + siga1 * P0(t) * phi * M^2 * P2(t) * Ks * siga2 * beta_SA - + siga1 * P0(t) * phi * M^2 * Mar * P5(t) * Ks * siga2^2 - + siga1 * P0(t) * phi * M^2 * Ks * beta * siga2^2 - + siga1 * P0(t) * phi * M * P2(t) * Mar * Ks^2 * beta_SA - + 2 * siga1 * P0(t) * phi * M * P2(t) * Mar * Ks * siga2 * beta_SA + + siga1 * P0(t) * phi * M * P2(t) * Ks^2 * siga2 * beta_SA + + 2 * siga1 * P0(t) * phi * M * P2(t) * Ks * siga2^2 * beta_SA + + siga1 * P0(t) * M^2 * P2(t) * Mar * Ks * beta_SA + + siga1 * P0(t) * M^2 * P2(t) * Ks * siga2 * beta_SA + + siga1 * P0(t) * M^2 * Mar * P5(t) * Ks * siga2^2 + + siga1 * P0(t) * M^2 * Ks * beta * siga2^2 + + siga1 * P0(t) * M * P2(t) * Mar * Ks^2 * beta_SA + + 2 * siga1 * P0(t) * M * P2(t) * Mar * Ks * siga2 * beta_SA + + siga1 * P0(t) * M * P2(t) * Ks^2 * siga2 * beta_SA - + siga1 * P1(t)^2 * beta_SI * phi * M * Mar * Ks * siga2 - + siga1 * P1(t)^2 * beta_SI * phi * M * Mar * siga2^2 - + siga1 * P1(t)^2 * beta_SI * phi * M * Ks^2 * siga2 - + siga1 * P1(t)^2 * beta_SI * phi * M * Ks * siga2^2 - + siga1 * P1(t)^2 * beta_SI * phi * Mar * Ks * siga2^2 - + siga1 * P1(t)^2 * beta_SI * phi * Ks^2 * siga2^2 + + siga1 * P1(t)^2 * beta_SI * M * Mar * Ks * siga2 + + siga1 * P1(t)^2 * beta_SI * M * Mar * siga2^2 + + siga1 * P1(t)^2 * beta_SI * M * Ks^2 * siga2 + + siga1 * P1(t)^2 * beta_SI * M * Ks * siga2^2 + + siga1 * P1(t)^2 * beta_SI * Mar * Ks * siga2^2 + + siga1 * P1(t)^2 * beta_SI * Ks^2 * siga2^2 - + siga1 * P1(t)^2 * phi * M^2 * Mar * Ks * beta_SA - + siga1 * P1(t)^2 * phi * M^2 * Mar * siga2 * beta_SA + + siga1 * P1(t)^2 * phi * M^2 * Ks * siga2 * beta_SA + + siga1 * P1(t)^2 * phi * M^2 * siga2^2 * beta_SA - + siga1 * P1(t)^2 * phi * M * Mar * Ks^2 * beta_SA - + 2 * siga1 * P1(t)^2 * phi * M * Mar * Ks * siga2 * beta_SA + + siga1 * P1(t)^2 * phi * M * Ks^2 * siga2 * beta_SA + + 2 * siga1 * P1(t)^2 * phi * M * Ks * siga2^2 * beta_SA - + siga1 * P1(t)^2 * phi * Mar * Ks^2 * siga2 * beta_SA + + siga1 * P1(t)^2 * phi * Ks^2 * siga2^2 * beta_SA + + siga1 * P1(t)^2 * M^2 * Mar * Ks * beta_SA + + siga1 * P1(t)^2 * M^2 * Mar * siga2 * beta_SA + + siga1 * P1(t)^2 * M^2 * Ks^2 * beta_SA + + siga1 * P1(t)^2 * M^2 * Ks * siga2 * beta_SA + + siga1 * P1(t)^2 * M * Mar * Ks^2 * beta_SA + + 2 * siga1 * P1(t)^2 * M * Mar * Ks * siga2 * beta_SA + + siga1 * P1(t)^2 * M * Ks^2 * siga2 * beta_SA + + siga1 * P1(t)^2 * Mar * Ks^2 * siga2 * beta_SA - + siga1 * P1(t) * beta_SI * P3(t) * phi * Mar * siga2 - + siga1 * P1(t) * beta_SI * P3(t) * phi * Ks * siga2 + + siga1 * P1(t) * beta_SI * P3(t) * Mar * siga2 + + siga1 * P1(t) * beta_SI * P3(t) * Ks * siga2 - + siga1 * P1(t) * beta_SI * phi * M * P2(t) * Mar * siga2 - + 2 * siga1 * P1(t) * beta_SI * phi * M * P2(t) * Ks * siga2 - + siga1 * P1(t) * beta_SI * phi * M * P2(t) * siga2^2 - + siga1 * P1(t) * beta_SI * phi * P2(t) * Mar * Ks * siga2 - + siga1 * P1(t) * beta_SI * phi * P2(t) * Mar * siga2^2 - + siga1 * P1(t) * beta_SI * phi * P2(t) * Ks^2 * siga2 - + 2 * siga1 * P1(t) * beta_SI * phi * P2(t) * Ks * siga2^2 + + siga1 * P1(t) * beta_SI * M * P2(t) * Mar * siga2 + + 2 * siga1 * P1(t) * beta_SI * M * P2(t) * Ks * siga2 + + siga1 * P1(t) * beta_SI * M * P2(t) * siga2^2 + + siga1 * P1(t) * beta_SI * P2(t) * Mar * Ks * siga2 + + siga1 * P1(t) * beta_SI * P2(t) * Mar * siga2^2 + + siga1 * P1(t) * beta_SI * P2(t) * Ks^2 * siga2 + + 2 * siga1 * P1(t) * beta_SI * P2(t) * Ks * siga2^2 - + siga1 * P1(t) * P3(t) * phi * M * Mar * beta_SA + + siga1 * P1(t) * P3(t) * phi * M * siga2 * beta_SA - + siga1 * P1(t) * P3(t) * phi * Mar * Ks * beta_SA + + siga1 * P1(t) * P3(t) * phi * Ks * siga2 * beta_SA + + siga1 * P1(t) * P3(t) * M * Mar * beta_SA + + 2 * siga1 * P1(t) * P3(t) * M * Ks * beta_SA + + siga1 * P1(t) * P3(t) * M * siga2 * beta_SA + + siga1 * P1(t) * P3(t) * Mar * Ks * beta_SA + + siga1 * P1(t) * P3(t) * Ks * siga2 * beta_SA - + siga1 * P1(t) * phi * M^2 * P2(t) * Mar * beta_SA + + siga1 * P1(t) * phi * M^2 * P2(t) * siga2 * beta_SA - + siga1 * P1(t) * phi * M^2 * Mar * P5(t) * Ks * siga2 - + siga1 * P1(t) * phi * M^2 * Mar * P5(t) * siga2^2 - + siga1 * P1(t) * phi * M^2 * Ks * beta * siga2 - + siga1 * P1(t) * phi * M^2 * Ks * siga2^2 - + siga1 * P1(t) * phi * M^2 * beta * siga2^2 - + 3 * siga1 * P1(t) * phi * M * P2(t) * Mar * Ks * beta_SA - + 2 * siga1 * P1(t) * phi * M * P2(t) * Mar * siga2 * beta_SA + + 3 * siga1 * P1(t) * phi * M * P2(t) * Ks * siga2 * beta_SA + + 2 * siga1 * P1(t) * phi * M * P2(t) * siga2^2 * beta_SA - + siga1 * P1(t) * phi * M * Mar * P5(t) * Ks * siga2^2 - + siga1 * P1(t) * phi * M * Ks * beta * siga2^2 - + siga1 * P1(t) * phi * P2(t) * Mar * Ks^2 * beta_SA - + 2 * siga1 * P1(t) * phi * P2(t) * Mar * Ks * siga2 * beta_SA + + siga1 * P1(t) * phi * P2(t) * Ks^2 * siga2 * beta_SA + + 2 * siga1 * P1(t) * phi * P2(t) * Ks * siga2^2 * beta_SA + + siga1 * P1(t) * M^2 * P2(t) * Mar * beta_SA + + 2 * siga1 * P1(t) * M^2 * P2(t) * Ks * beta_SA + + siga1 * P1(t) * M^2 * P2(t) * siga2 * beta_SA + + siga1 * P1(t) * M^2 * Mar * P5(t) * Ks * siga2 + + siga1 * P1(t) * M^2 * Mar * P5(t) * siga2^2 + + siga1 * P1(t) * M^2 * Ks * beta * siga2 + + siga1 * P1(t) * M^2 * Ks * siga2^2 + + siga1 * P1(t) * M^2 * beta * siga2^2 + + 3 * siga1 * P1(t) * M * P2(t) * Mar * Ks * beta_SA + + 2 * siga1 * P1(t) * M * P2(t) * Mar * siga2 * beta_SA + + 2 * siga1 * P1(t) * M * P2(t) * Ks^2 * beta_SA + + 3 * siga1 * P1(t) * M * P2(t) * Ks * siga2 * beta_SA + + siga1 * P1(t) * M * Mar * P5(t) * Ks * siga2^2 + + siga1 * P1(t) * M * Ks * beta * siga2^2 + + siga1 * P1(t) * P2(t) * Mar * Ks^2 * beta_SA + + 2 * siga1 * P1(t) * P2(t) * Mar * Ks * siga2 * beta_SA + + siga1 * P1(t) * P2(t) * Ks^2 * siga2 * beta_SA - + siga1 * beta_SI * P3(t) * phi * P2(t) * siga2 + + siga1 * beta_SI * P3(t) * P2(t) * siga2 - + siga1 * beta_SI * phi * M * P2(t)^2 * siga2 - + siga1 * beta_SI * phi * P2(t)^2 * Ks * siga2 - + siga1 * beta_SI * phi * P2(t)^2 * siga2^2 + + siga1 * beta_SI * M * P2(t)^2 * siga2 + + siga1 * beta_SI * P2(t)^2 * Ks * siga2 + + siga1 * beta_SI * P2(t)^2 * siga2^2 + + siga1 * P3(t)^2 * beta_SA - siga1 * P3(t) * phi * M^2 * siga2 - + siga1 * P3(t) * phi * M * Mar * P5(t) * siga2 - + siga1 * P3(t) * phi * M * Ks * siga2 - + siga1 * P3(t) * phi * M * beta * siga2 - + siga1 * P3(t) * phi * M * siga2^2 - + siga1 * P3(t) * phi * P2(t) * Mar * beta_SA + + siga1 * P3(t) * phi * P2(t) * siga2 * beta_SA + + siga1 * P3(t) * M^2 * siga2 + + 2 * siga1 * P3(t) * M * P2(t) * beta_SA + + siga1 * P3(t) * M * Mar * P5(t) * siga2 + + siga1 * P3(t) * M * Ks * siga2 + + siga1 * P3(t) * M * beta * siga2 + + siga1 * P3(t) * M * siga2^2 + + siga1 * P3(t) * P2(t) * Mar * beta_SA + + 2 * siga1 * P3(t) * P2(t) * Ks * beta_SA + + siga1 * P3(t) * P2(t) * siga2 * beta_SA - + siga1 * phi * M^2 * P2(t) * Mar * P5(t) * siga2 - + siga1 * phi * M^2 * P2(t) * Ks * siga2 - + siga1 * phi * M^2 * P2(t) * beta * siga2 - + siga1 * phi * M^2 * P2(t) * siga2^2 - siga1 * phi * M * P4(t) * siga2 - + siga1 * phi * M * P2(t)^2 * Mar * beta_SA + + siga1 * phi * M * P2(t)^2 * siga2 * beta_SA - + siga1 * phi * M * P2(t) * Mar * P5(t) * Ks * siga2 - + siga1 * phi * M * P2(t) * Mar * P5(t) * siga2^2 - + siga1 * phi * M * P2(t) * Ks * beta * siga2 - + siga1 * phi * M * P2(t) * Ks * siga2^2 - + siga1 * phi * M * P2(t) * beta * siga2^2 - + siga1 * phi * P2(t)^2 * Mar * Ks * beta_SA - + siga1 * phi * P2(t)^2 * Mar * siga2 * beta_SA + + siga1 * phi * P2(t)^2 * Ks * siga2 * beta_SA + + siga1 * phi * P2(t)^2 * siga2^2 * beta_SA + + siga1 * M^2 * P2(t)^2 * beta_SA + + siga1 * M^2 * P2(t) * Mar * P5(t) * siga2 + + siga1 * M^2 * P2(t) * Ks * siga2 + + siga1 * M^2 * P2(t) * beta * siga2 + + siga1 * M^2 * P2(t) * siga2^2 + + siga1 * M * P4(t) * siga2 + + siga1 * M * P2(t)^2 * Mar * beta_SA + + 2 * siga1 * M * P2(t)^2 * Ks * beta_SA + + siga1 * M * P2(t)^2 * siga2 * beta_SA + + siga1 * M * P2(t) * Mar * P5(t) * Ks * siga2 + + siga1 * M * P2(t) * Mar * P5(t) * siga2^2 + + siga1 * M * P2(t) * Ks * beta * siga2 + + siga1 * M * P2(t) * Ks * siga2^2 + + siga1 * M * P2(t) * beta * siga2^2 + + siga1 * P2(t)^2 * Mar * Ks * beta_SA + + siga1 * P2(t)^2 * Mar * siga2 * beta_SA + + siga1 * P2(t)^2 * Ks^2 * beta_SA + + siga1 * P2(t)^2 * Ks * siga2 * beta_SA - + P0(t) * P1(t) * beta_SI * phi * M * Mar * Ks^2 * siga2^2 + + P0(t) * P1(t) * beta_SI * M * Mar * Ks^2 * siga2^2 - + P0(t) * P1(t) * phi * M^2 * Mar * Ks^2 * siga2 * beta_SA + + P0(t) * P1(t) * phi * M^2 * Ks^2 * siga2^2 * beta_SA + + P0(t) * P1(t) * M^2 * Mar * Ks^2 * siga2 * beta_SA - + P0(t) * beta_SI * P3(t) * phi * M * Mar * Ks * siga2 - + P0(t) * beta_SI * P3(t) * phi * Mar * Ks^2 * siga2 - + P0(t) * beta_SI * P3(t) * phi * Mar * Ks * siga2^2 + + P0(t) * beta_SI * P3(t) * M * Mar * Ks * siga2 + + P0(t) * beta_SI * P3(t) * Mar * Ks^2 * siga2 + + P0(t) * beta_SI * P3(t) * Mar * Ks * siga2^2 - + P0(t) * beta_SI * phi * alpa * Mar * Ks * siga2 - + P0(t) * beta_SI * phi * M * P2(t) * Mar * Ks^2 * siga2 - + P0(t) * beta_SI * phi * M * P2(t) * Mar * Ks * siga2^2 - + P0(t) * beta_SI * phi * P4(t) * Mar * Ks * siga2 - + P0(t) * beta_SI * phi * P2(t) * Mar * Ks^2 * siga2^2 + + P0(t) * beta_SI * alpa * Mar * Ks * siga2 + + P0(t) * beta_SI * M * P2(t) * Mar * Ks^2 * siga2 + + P0(t) * beta_SI * M * P2(t) * Mar * Ks * siga2^2 + + P0(t) * beta_SI * P4(t) * Mar * Ks * siga2 + + P0(t) * beta_SI * P2(t) * Mar * Ks^2 * siga2^2 - + P0(t) * P3(t) * phi * M^2 * Mar * Ks * beta_SA + + P0(t) * P3(t) * phi * M^2 * Ks * siga2 * beta_SA - + P0(t) * P3(t) * phi * M * Mar * Ks^2 * beta_SA - + P0(t) * P3(t) * phi * M * Mar * Ks * siga2 * beta_SA + + P0(t) * P3(t) * phi * M * Ks^2 * siga2 * beta_SA + + P0(t) * P3(t) * phi * M * Ks * siga2^2 * beta_SA + + P0(t) * P3(t) * M^2 * Mar * Ks * beta_SA + + P0(t) * P3(t) * M * Mar * Ks^2 * beta_SA + + P0(t) * P3(t) * M * Mar * Ks * siga2 * beta_SA - + P0(t) * phi * alpa * M * Mar * Ks * beta_SA + + P0(t) * phi * alpa * M * Ks * siga2 * beta_SA - + P0(t) * phi * M^2 * P2(t) * Mar * Ks^2 * beta_SA - + P0(t) * phi * M^2 * P2(t) * Mar * Ks * siga2 * beta_SA + + P0(t) * phi * M^2 * P2(t) * Ks^2 * siga2 * beta_SA + + P0(t) * phi * M^2 * P2(t) * Ks * siga2^2 * beta_SA - + P0(t) * phi * M * P4(t) * Mar * Ks * beta_SA + + P0(t) * phi * M * P4(t) * Ks * siga2 * beta_SA - + P0(t) * phi * M * P2(t) * Mar * Ks^2 * siga2 * beta_SA + + P0(t) * phi * M * P2(t) * Ks^2 * siga2^2 * beta_SA + + P0(t) * alpa * M * Mar * Ks * beta_SA + + P0(t) * M^2 * P2(t) * Mar * Ks^2 * beta_SA + + P0(t) * M^2 * P2(t) * Mar * Ks * siga2 * beta_SA + + P0(t) * M * P4(t) * Mar * Ks * beta_SA + + P0(t) * M * P2(t) * Mar * Ks^2 * siga2 * beta_SA - + P1(t)^2 * beta_SI * phi * M * Mar * Ks * siga2^2 - + P1(t)^2 * beta_SI * phi * M * Ks^2 * siga2^2 + + P1(t)^2 * beta_SI * M * Mar * Ks * siga2^2 + + P1(t)^2 * beta_SI * M * Ks^2 * siga2^2 - + P1(t)^2 * phi * M^2 * Mar * Ks * siga2 * beta_SA + + P1(t)^2 * phi * M^2 * Ks * siga2^2 * beta_SA - + P1(t)^2 * phi * M * Mar * Ks^2 * siga2 * beta_SA + + P1(t)^2 * phi * M * Ks^2 * siga2^2 * beta_SA + + P1(t)^2 * M^2 * Mar * Ks * siga2 * beta_SA + + P1(t)^2 * M^2 * Ks^2 * siga2 * beta_SA + + P1(t)^2 * M * Mar * Ks^2 * siga2 * beta_SA - + P1(t) * beta_SI * P3(t) * phi * M * Mar * siga2 - + P1(t) * beta_SI * P3(t) * phi * M * Ks * siga2 - + P1(t) * beta_SI * P3(t) * phi * Mar * Ks * siga2 - + P1(t) * beta_SI * P3(t) * phi * Mar * siga2^2 - + P1(t) * beta_SI * P3(t) * phi * Ks^2 * siga2 - + P1(t) * beta_SI * P3(t) * phi * Ks * siga2^2 + + P1(t) * beta_SI * P3(t) * M * Mar * siga2 + + P1(t) * beta_SI * P3(t) * M * Ks * siga2 + + P1(t) * beta_SI * P3(t) * Mar * Ks * siga2 + + P1(t) * beta_SI * P3(t) * Mar * siga2^2 + + P1(t) * beta_SI * P3(t) * Ks^2 * siga2 + + P1(t) * beta_SI * P3(t) * Ks * siga2^2 - + P1(t) * beta_SI * phi * alpa * Mar * siga2 - + P1(t) * beta_SI * phi * alpa * Ks * siga2 - + P1(t) * beta_SI * phi * M * P2(t) * Mar * Ks * siga2 - + P1(t) * beta_SI * phi * M * P2(t) * Mar * siga2^2 - + P1(t) * beta_SI * phi * M * P2(t) * Ks^2 * siga2 - + 2 * P1(t) * beta_SI * phi * M * P2(t) * Ks * siga2^2 - + P1(t) * beta_SI * phi * P4(t) * Mar * siga2 - + P1(t) * beta_SI * phi * P4(t) * Ks * siga2 - + P1(t) * beta_SI * phi * P2(t) * Mar * Ks * siga2^2 - + P1(t) * beta_SI * phi * P2(t) * Ks^2 * siga2^2 + + P1(t) * beta_SI * alpa * Mar * siga2 + + P1(t) * beta_SI * alpa * Ks * siga2 + + P1(t) * beta_SI * M * P2(t) * Mar * Ks * siga2 + + P1(t) * beta_SI * M * P2(t) * Mar * siga2^2 + + P1(t) * beta_SI * M * P2(t) * Ks^2 * siga2 + + 2 * P1(t) * beta_SI * M * P2(t) * Ks * siga2^2 + + P1(t) * beta_SI * P4(t) * Mar * siga2 + + P1(t) * beta_SI * P4(t) * Ks * siga2 + + P1(t) * beta_SI * P2(t) * Mar * Ks * siga2^2 + + P1(t) * beta_SI * P2(t) * Ks^2 * siga2^2 - + P1(t) * P3(t) * phi * M^2 * Mar * beta_SA + + P1(t) * P3(t) * phi * M^2 * siga2 * beta_SA - + 2 * P1(t) * P3(t) * phi * M * Mar * Ks * beta_SA - + P1(t) * P3(t) * phi * M * Mar * siga2 * beta_SA + + 2 * P1(t) * P3(t) * phi * M * Ks * siga2 * beta_SA + + P1(t) * P3(t) * phi * M * siga2^2 * beta_SA - + P1(t) * P3(t) * phi * Mar * Ks^2 * beta_SA - + P1(t) * P3(t) * phi * Mar * Ks * siga2 * beta_SA + + P1(t) * P3(t) * phi * Ks^2 * siga2 * beta_SA + + P1(t) * P3(t) * phi * Ks * siga2^2 * beta_SA + + P1(t) * P3(t) * M^2 * Mar * beta_SA + + P1(t) * P3(t) * M^2 * Ks * beta_SA + + 2 * P1(t) * P3(t) * M * Mar * Ks * beta_SA + + P1(t) * P3(t) * M * Mar * siga2 * beta_SA + + P1(t) * P3(t) * M * Ks^2 * beta_SA + + 2 * P1(t) * P3(t) * M * Ks * siga2 * beta_SA + + P1(t) * P3(t) * Mar * Ks^2 * beta_SA + + P1(t) * P3(t) * Mar * Ks * siga2 * beta_SA - + P1(t) * phi * alpa * M * Mar * beta_SA + + P1(t) * phi * alpa * M * siga2 * beta_SA - + P1(t) * phi * alpa * Mar * Ks * beta_SA + + P1(t) * phi * alpa * Ks * siga2 * beta_SA - + P1(t) * phi * M^2 * P2(t) * Mar * Ks * beta_SA - + P1(t) * phi * M^2 * P2(t) * Mar * siga2 * beta_SA + + P1(t) * phi * M^2 * P2(t) * Ks * siga2 * beta_SA + + P1(t) * phi * M^2 * P2(t) * siga2^2 * beta_SA - + P1(t) * phi * M^2 * Mar * P5(t) * Ks * siga2^2 - + P1(t) * phi * M^2 * Ks * beta * siga2^2 - + P1(t) * phi * M * P4(t) * Mar * beta_SA + + P1(t) * phi * M * P4(t) * siga2 * beta_SA - + P1(t) * phi * M * P2(t) * Mar * Ks^2 * beta_SA - + 3 * P1(t) * phi * M * P2(t) * Mar * Ks * siga2 * beta_SA + + P1(t) * phi * M * P2(t) * Ks^2 * siga2 * beta_SA + + 3 * P1(t) * phi * M * P2(t) * Ks * siga2^2 * beta_SA - + P1(t) * phi * P4(t) * Mar * Ks * beta_SA + + P1(t) * phi * P4(t) * Ks * siga2 * beta_SA - + P1(t) * phi * P2(t) * Mar * Ks^2 * siga2 * beta_SA + + P1(t) * phi * P2(t) * Ks^2 * siga2^2 * beta_SA + + P1(t) * alpa * M * Mar * beta_SA + + P1(t) * alpa * M * Ks * beta_SA + + P1(t) * alpa * Mar * Ks * beta_SA + + P1(t) * M^2 * P2(t) * Mar * Ks * beta_SA + + P1(t) * M^2 * P2(t) * Mar * siga2 * beta_SA + + P1(t) * M^2 * P2(t) * Ks^2 * beta_SA + + 2 * P1(t) * M^2 * P2(t) * Ks * siga2 * beta_SA + + P1(t) * M^2 * Mar * P5(t) * Ks * siga2^2 + + P1(t) * M^2 * Ks * beta * siga2^2 + + P1(t) * M * P4(t) * Mar * beta_SA + + P1(t) * M * P4(t) * Ks * beta_SA + + P1(t) * M * P2(t) * Mar * Ks^2 * beta_SA + + 3 * P1(t) * M * P2(t) * Mar * Ks * siga2 * beta_SA + + 2 * P1(t) * M * P2(t) * Ks^2 * siga2 * beta_SA + + P1(t) * P4(t) * Mar * Ks * beta_SA + + P1(t) * P2(t) * Mar * Ks^2 * siga2 * beta_SA - + beta_SI * P3(t) * phi * M * P2(t) * siga2 - + beta_SI * P3(t) * phi * P2(t) * Ks * siga2 - + beta_SI * P3(t) * phi * P2(t) * siga2^2 + + beta_SI * P3(t) * M * P2(t) * siga2 + + beta_SI * P3(t) * P2(t) * Ks * siga2 + + beta_SI * P3(t) * P2(t) * siga2^2 - + beta_SI * phi * alpa * P2(t) * siga2 - + beta_SI * phi * M * P2(t)^2 * Ks * siga2 - + beta_SI * phi * M * P2(t)^2 * siga2^2 - + beta_SI * phi * P4(t) * P2(t) * siga2 - + beta_SI * phi * P2(t)^2 * Ks * siga2^2 + + beta_SI * alpa * P2(t) * siga2 + + beta_SI * M * P2(t)^2 * Ks * siga2 + + beta_SI * M * P2(t)^2 * siga2^2 + + beta_SI * P4(t) * P2(t) * siga2 + + beta_SI * P2(t)^2 * Ks * siga2^2 + + P3(t)^2 * M * beta_SA + + P3(t)^2 * Ks * beta_SA + + P3(t)^2 * siga2 * beta_SA - P3(t) * phi * M^2 * Mar * P5(t) * siga2 - + P3(t) * phi * M^2 * Ks * siga2 - P3(t) * phi * M^2 * beta * siga2 - + P3(t) * phi * M^2 * siga2^2 - P3(t) * phi * M * P2(t) * Mar * beta_SA + + P3(t) * phi * M * P2(t) * siga2 * beta_SA - + P3(t) * phi * M * Mar * P5(t) * Ks * siga2 - + P3(t) * phi * M * Mar * P5(t) * siga2^2 - + P3(t) * phi * M * Ks * beta * siga2 - P3(t) * phi * M * Ks * siga2^2 - + P3(t) * phi * M * beta * siga2^2 - + P3(t) * phi * P2(t) * Mar * Ks * beta_SA - + P3(t) * phi * P2(t) * Mar * siga2 * beta_SA + + P3(t) * phi * P2(t) * Ks * siga2 * beta_SA + + P3(t) * phi * P2(t) * siga2^2 * beta_SA + + P3(t) * alpa * beta_SA + + P3(t) * M^2 * P2(t) * beta_SA + + P3(t) * M^2 * Mar * P5(t) * siga2 + + P3(t) * M^2 * Ks * siga2 + + P3(t) * M^2 * beta * siga2 + + P3(t) * M^2 * siga2^2 + + P3(t) * M * P2(t) * Mar * beta_SA + + 3 * P3(t) * M * P2(t) * Ks * beta_SA + + 2 * P3(t) * M * P2(t) * siga2 * beta_SA + + P3(t) * M * Mar * P5(t) * Ks * siga2 + + P3(t) * M * Mar * P5(t) * siga2^2 + + P3(t) * M * Ks * beta * siga2 + + P3(t) * M * Ks * siga2^2 + + P3(t) * M * beta * siga2^2 + + P3(t) * P4(t) * beta_SA + + P3(t) * P2(t) * Mar * Ks * beta_SA + + P3(t) * P2(t) * Mar * siga2 * beta_SA + + P3(t) * P2(t) * Ks^2 * beta_SA + + 2 * P3(t) * P2(t) * Ks * siga2 * beta_SA - + phi * alpa * M * Mar * P5(t) * siga2 - phi * alpa * M * beta * siga2 - + phi * alpa * P2(t) * Mar * beta_SA + + phi * alpa * P2(t) * siga2 * beta_SA - phi * M^2 * P4(t) * siga2 - + phi * M^2 * P2(t) * Mar * P5(t) * Ks * siga2 - + phi * M^2 * P2(t) * Mar * P5(t) * siga2^2 - + phi * M^2 * P2(t) * Ks * beta * siga2 - + phi * M^2 * P2(t) * Ks * siga2^2 - phi * M^2 * P2(t) * beta * siga2^2 - + phi * M * P4(t) * Mar * P5(t) * siga2 - phi * M * P4(t) * Ks * siga2 - + phi * M * P4(t) * beta * siga2 - phi * M * P4(t) * siga2^2 - + phi * M * P2(t)^2 * Mar * Ks * beta_SA - + phi * M * P2(t)^2 * Mar * siga2 * beta_SA + + phi * M * P2(t)^2 * Ks * siga2 * beta_SA + + phi * M * P2(t)^2 * siga2^2 * beta_SA - + phi * M * P2(t) * Mar * P5(t) * Ks * siga2^2 - + phi * M * P2(t) * Ks * beta * siga2^2 - + phi * P4(t) * P2(t) * Mar * beta_SA + + phi * P4(t) * P2(t) * siga2 * beta_SA - + phi * P2(t)^2 * Mar * Ks * siga2 * beta_SA + + phi * P2(t)^2 * Ks * siga2^2 * beta_SA + + alpa * M * P2(t) * beta_SA + + alpa * M * Mar * P5(t) * siga2 + + alpa * M * beta * siga2 + + alpa * P2(t) * Mar * beta_SA + + alpa * P2(t) * Ks * beta_SA + + M^2 * P4(t) * siga2 + + M^2 * P2(t)^2 * Ks * beta_SA + + M^2 * P2(t)^2 * siga2 * beta_SA + + M^2 * P2(t) * Mar * P5(t) * Ks * siga2 + + M^2 * P2(t) * Mar * P5(t) * siga2^2 + + M^2 * P2(t) * Ks * beta * siga2 + + M^2 * P2(t) * Ks * siga2^2 + + M^2 * P2(t) * beta * siga2^2 + + M * P4(t) * P2(t) * beta_SA + + M * P4(t) * Mar * P5(t) * siga2 + + M * P4(t) * Ks * siga2 + + M * P4(t) * beta * siga2 + + M * P4(t) * siga2^2 + + M * P2(t)^2 * Mar * Ks * beta_SA + + M * P2(t)^2 * Mar * siga2 * beta_SA + + M * P2(t)^2 * Ks^2 * beta_SA + + 2 * M * P2(t)^2 * Ks * siga2 * beta_SA + + M * P2(t) * Mar * P5(t) * Ks * siga2^2 + + M * P2(t) * Ks * beta * siga2^2 + + P4(t) * P2(t) * Mar * beta_SA + + P4(t) * P2(t) * Ks * beta_SA + + P2(t)^2 * Mar * Ks * siga2 * beta_SA + + P2(t)^2 * Ks^2 * siga2 * beta_SA + ) // (phi * M * siga2 - M * siga2), + P1'(t) = P2(t), + P2'(t) = P3(t), + y(t) = P0(t) +) +ident_funcs = [ + Mar, + Ks, + alpa, + (siga1 + phi * Mar - Mar) // phi, + (siga1 * phi - siga1 - phi * Mar + Mar) // (siga1 * phi * beta_SA), + ( + siga1 * beta_SA + beta_SI * phi * siga2 + phi * Mar * beta_SA - + phi * siga2 * beta_SA - Mar * beta_SA + ) // (phi * M * siga2), + ( + siga1 * phi * M * siga2 - siga1 * M * siga2 - phi * M * Mar * siga2 + + M * Mar * siga2 + ) // (siga1 * beta_SA + phi * Mar * beta_SA - Mar * beta_SA), + ( + siga1 * beta_SI * phi * siga2 - siga1 * beta_SI * siga2 - + siga1 * phi * siga2 * beta_SA - siga1 * M * beta_SA - + beta_SI * phi * Mar * siga2 + beta_SI * Mar * siga2 - + phi * M * Mar * beta_SA + M * Mar * beta_SA + ) // (siga1 * beta_SA + phi * Mar * beta_SA - Mar * beta_SA), + ( + siga1^2 * beta_SA + siga1 * beta_SI * phi * siga2 - siga1 * beta_SI * siga2 + siga1 * phi * Mar * beta_SA - + siga1 * phi * siga2 * beta_SA - siga1 * Mar * beta_SA + + siga1 * siga2 * beta_SA + + beta_SI * phi * M * siga2 - beta_SI * phi * Mar * siga2 + + beta_SI * phi * siga2^2 + + beta_SI * Mar * siga2 + + phi * Mar * siga2 * beta_SA - phi * siga2^2 * beta_SA - + Mar * siga2 * beta_SA + ) // (phi * M * siga2), +] +# Really large and takes a lot of time, so commented +# push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +### +# Cases with states + +ode = StructuralIdentifiability.@ODEmodel(x'(t) = x(t), y(t) = x(t)) +T = typeof(x) +ident_funcs = [x] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs, with_states = true)) + +ode = StructuralIdentifiability.@ODEmodel(x'(t) = a * x(t) + u(t), y(t) = x(t)) +ident_funcs = [a, x] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = a * x1(t) - b * x1(t) * x2(t), + x2'(t) = -c * x2(t) + d * x1(t) * x2(t), + y(t) = x1(t) +) +ident_funcs = [x1, c, d, a, b * x2] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = a * x1(t) - b * x1(t) * x2(t), + x2'(t) = -c * x2(t) + d * x1(t) * x2(t), + y(t) = x1(t) +) +ident_funcs = [x1, c, d, a, b * x2] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +# Diagonal with simple spectrum and observable states +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = λ1 * x1(t) + β1 * u1(t), + x2'(t) = λ2 * x2(t) + β2 * u2(t), + x3'(t) = λ3 * x3(t) + β3 * u3(t), + y(t) = x1(t) + x2(t) + x3(t) +) +ident_funcs = [λ1, λ2, λ3, β1, β2, β3, x1, x2, x3] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = x1(t) + Θ * x2(t), + x2'(t) = 0, + y(t) = x1(t) +) +ident_funcs = [x1, x2 * Θ] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = α * x2(t), + x2'(t) = x3(t), + x3'(t) = C, + y(t) = x1(t) +) +ident_funcs = [x1, α * x2, α * x3, α * C] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = α * (x1 - x2), + x2'(t) = α * (x1 + x2), + y(t) = (x1^2 + x2^2) // 2, +) +ident_funcs = [α, x1^2 + x2^2] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +ode = StructuralIdentifiability.@ODEmodel(x'(t) = a * x(t) + b * u(t), y(t) = c * x(t)) +ident_funcs = [b * c, a, x // b] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +# llw1987 model +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = -p1 * x1(t) + p2 * u(t), + x2'(t) = -p3 * x2(t) + p4 * u(t), + x3'(t) = -(p1 + p3) * x3(t) + (p4 * x1(t) + p2 * x2(t)) * u(t), + y1(t) = x3(t) +) +ident_funcs = [ + x3, + x2 * x1 // one(x1), + p3 * p1 // one(x1), + p2 * p4 // one(x1), + (p3 + p1) // one(x1), + (p2 * x2 + p4 * x1) // one(x1), + (p3 - p1) // (p2 * x2 - p4 * x1), +] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +# Regression test: Previously failed for with_states=true because of the bug in +# `linear_relations_between_normal_forms` +# Fujita +ode = StructuralIdentifiability.@ODEmodel( + EGFR'(t) = + EGFR_turnover * pro_EGFR(t) + EGF_EGFR(t) * reaction_1_k2 - + EGFR(t) * EGFR_turnover - EGF_EGFR(t) * reaction_1_k1, + pEGFR'(t) = + EGF_EGFR(t) * reaction_9_k1 - pEGFR(t) * reaction_4_k1 + + pEGFR_Akt(t) * reaction_2_k2 + + pEGFR_Akt(t) * reaction_3_k1 - Akt(t) * pEGFR(t) * reaction_2_k1, + pEGFR_Akt'(t) = + Akt(t) * pEGFR(t) * reaction_2_k1 - pEGFR_Akt(t) * reaction_3_k1 - + pEGFR_Akt(t) * reaction_2_k2, + Akt'(t) = + pAkt(t) * reaction_7_k1 + pEGFR_Akt(t) * reaction_2_k2 - + Akt(t) * pEGFR(t) * reaction_2_k1, + pAkt'(t) = + pAkt_S6(t) * reaction_5_k2 - pAkt(t) * reaction_7_k1 + + pAkt_S6(t) * reaction_6_k1 + + pEGFR_Akt(t) * reaction_3_k1 - S6(t) * pAkt(t) * reaction_5_k1, + S6'(t) = + pAkt_S6(t) * reaction_5_k2 + pS6(t) * reaction_8_k1 - + S6(t) * pAkt(t) * reaction_5_k1, + pAkt_S6'(t) = + S6(t) * pAkt(t) * reaction_5_k1 - pAkt_S6(t) * reaction_6_k1 - + pAkt_S6(t) * reaction_5_k2, + pS6'(t) = pAkt_S6(t) * reaction_6_k1 - pS6(t) * reaction_8_k1, + EGF_EGFR'(t) = + EGF_EGFR(t) * reaction_1_k1 - EGF_EGFR(t) * reaction_9_k1 - + EGF_EGFR(t) * reaction_1_k2, + y1(t) = a1 * (pEGFR(t) + pEGFR_Akt(t)), + y2(t) = a2 * (pAkt(t) + pAkt_S6(t)), + y3(t) = a3 * pS6(t) +) +ident_funcs = [ + reaction_8_k1, + reaction_2_k2, + reaction_5_k2, + reaction_3_k1, + reaction_6_k1, + reaction_7_k1, + reaction_4_k1, + reaction_2_k1 * pAkt_S6, + reaction_2_k1 * S6, + reaction_5_k1 * pAkt_S6, + pEGFR_Akt * reaction_2_k1, + Akt * reaction_2_k1, + a1 * pAkt_S6, + pEGFR * reaction_2_k1, + pAkt * reaction_2_k1, + a3 * pAkt_S6, + pS6 * reaction_2_k1, + a2 * pAkt_S6, + reaction_9_k1 * reaction_2_k1 * EGF_EGFR, + reaction_1_k1 - reaction_9_k1 - reaction_1_k2, +] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +# Bruno2016 model +ode = StructuralIdentifiability.@ODEmodel( + beta'(t) = -kbeta * beta(t), + cry'(t) = -cry(t) * kcrybeta - cry(t) * kcryOH, + zea'(t) = -zea(t) * kzea, + beta10'(t) = cry(t) * kcryOH - beta10(t) * kbeta10 + kbeta * beta(t), + OHbeta10'(t) = cry(t) * kcrybeta + zea(t) * kzea - OHbeta10(t) * kOHbeta10, + betaio'(t) = cry(t) * kcrybeta + beta10(t) * kbeta10 + kbeta * beta(t), + OHbetaio'(t) = cry(t) * kcryOH + zea(t) * kzea + OHbeta10(t) * kOHbeta10, + y1(t) = beta(t), + y2(t) = beta10(t) +) +ident_funcs = [beta10, beta, kbeta, kbeta10, cry * kcryOH, kcrybeta + kcryOH] +push!(test_cases, (ode = ode, with_states = true, ident_funcs = ident_funcs)) + +# STAT-5 model from +# MODELING THE NONLINEAR DYNAMICS OF CELLULAR SIGNAL TRANSDUCTION +# DOI: https://doi.org/10.1142/S0218127404010461 +ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = -k1 * x1 * EpoR_A, + x2'(t) = k1 * x1 * EpoR_A - k2 * x2^2, + x3'(t) = -k3 * x3 + 0.5 * k2 * x2^2, + x4'(t) = k3 * x3, + y1(t) = k5 * (x2 + 2x3), + y2(t) = k6 * (x1 + x2 + 2x3), + y3(t) = k7 * EpoR_A +) +ident_funcs = [k3, k1 // k7, k5 // k2, k6 // k2, k7 * EpoR_A] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs)) + +ode = @ODEmodel(x1'(t) = x1, x2'(t) = x2, y(t) = x1 + x2(t)) +ident_funcs = [x1 + x2] +push!(test_cases, (ode = ode, ident_funcs = ident_funcs, with_states = true)) + +# TODO: verify that Maple returns the same +@testset "Identifiable functions of parameters" begin + p = 0.99 + for case in test_cases + for simplify in [:weak, :standard] # :strong] + ode = case.ode + true_ident_funcs = case.ident_funcs + with_states = false + if haskey(case, :with_states) + with_states = case.with_states end + result_funcs = StructuralIdentifiability.find_identifiable_functions( + ode, + simplify = simplify, + with_states = with_states, + ) + + if isempty(true_ident_funcs) + @test isempty(result_funcs) + continue + end + + @test parent(numerator(result_funcs[1])) == parent(ode) + + R = parent(numerator(result_funcs[1])) + + @info "Test, result_funcs = \n$result_funcs" case simplify R with_states + + true_ident_funcs = map(f -> f // one(f), true_ident_funcs) + true_ident_funcs = map( + f -> StructuralIdentifiability.parent_ring_change(f, R), + true_ident_funcs, + ) + + # Check inclusion in + @test StructuralIdentifiability.fields_equal( + StructuralIdentifiability.RationalFunctionField(result_funcs), + StructuralIdentifiability.RationalFunctionField(true_ident_funcs), + p, + ) end end end diff --git a/test/io_cases.jl b/test/io_cases.jl index 762f1c55..220691f8 100644 --- a/test/io_cases.jl +++ b/test/io_cases.jl @@ -1,155 +1,153 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Checking io-equations: single output" begin - test_cases = [] +@testset "Checking io-equations: single output" begin + test_cases = [] - # 2-compartiment model - R, (y_0, y_1, y_2, u_0, u_1, a01, a12, a21) = polynomial_ring( - QQ, - ["y(t)_0", "y(t)_1", "y(t)_2", "u(t)_0", "u(t)_1", "a01", "a12", "a21"], - ) - correct = - y_2 + y_1 * a01 + y_1 * a21 + y_1 * a12 + y_0 * a01 * a12 - u_0 * a12 - u_1 - push!( - test_cases, - Dict( - :ode => @ODEmodel( - x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t) + u(t), - x1'(t) = a21 * x0(t) - a12 * x1(t), - y(t) = x0(t) - ), - :correct => correct, + # 2-compartiment model + R, (y_0, y_1, y_2, u_0, u_1, a01, a12, a21) = polynomial_ring( + QQ, + ["y(t)_0", "y(t)_1", "y(t)_2", "u(t)_0", "u(t)_1", "a01", "a12", "a21"], + ) + correct = + y_2 + y_1 * a01 + y_1 * a21 + y_1 * a12 + y_0 * a01 * a12 - u_0 * a12 - u_1 + push!( + test_cases, + Dict( + :ode => @ODEmodel( + x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t) + u(t), + x1'(t) = a21 * x0(t) - a12 * x1(t), + y(t) = x0(t) ), - ) + :correct => correct, + ), + ) - #--------------------------------------- - # Chen-Lee model - R, (y1_0, y1_1, y1_2, y1_3, a, b, c) = - polynomial_ring(QQ, ["y1(t)_0", "y1(t)_1", "y1(t)_2", "y1(t)_3", "a", "b", "c"]) - correct = - 9 * y1_3^2 * y1_0^2 - 18 * y1_3 * y1_2 * y1_1 * y1_0 - - 18 * y1_3 * y1_2 * y1_0^2 * a - 36 * y1_3 * y1_2 * y1_0^2 * b - - 36 * y1_3 * y1_2 * y1_0^2 * c + - 18 * y1_3 * y1_1^2 * y1_0 * a + - 18 * y1_3 * y1_1^2 * y1_0 * b + - 18 * y1_3 * y1_1^2 * y1_0 * c - 24 * y1_3 * y1_1 * y1_0^4 + - 18 * y1_3 * y1_1 * y1_0^2 * a * b + - 18 * y1_3 * y1_1 * y1_0^2 * a * c + - 18 * y1_3 * y1_1 * y1_0^2 * b^2 + - 36 * y1_3 * y1_1 * y1_0^2 * b * c + - 18 * y1_3 * y1_1 * y1_0^2 * c^2 + - 24 * y1_3 * y1_0^5 * a - 18 * y1_3 * y1_0^3 * a * b^2 - - 36 * y1_3 * y1_0^3 * a * b * c - 18 * y1_3 * y1_0^3 * a * c^2 + - 9 * y1_2^2 * y1_1^2 + - 18 * y1_2^2 * y1_1 * y1_0 * a + - 36 * y1_2^2 * y1_1 * y1_0 * b + - 36 * y1_2^2 * y1_1 * y1_0 * c + - 9 * y1_2^2 * y1_0^2 * a^2 + - 36 * y1_2^2 * y1_0^2 * a * b + - 36 * y1_2^2 * y1_0^2 * a * c + - 27 * y1_2^2 * y1_0^2 * b^2 + - 90 * y1_2^2 * y1_0^2 * b * c + - 27 * y1_2^2 * y1_0^2 * c^2 - 18 * y1_2 * y1_1^3 * a - 18 * y1_2 * y1_1^3 * b - - 18 * y1_2 * y1_1^3 * c + 24 * y1_2 * y1_1^2 * y1_0^3 - - 18 * y1_2 * y1_1^2 * y1_0 * a^2 - 72 * y1_2 * y1_1^2 * y1_0 * a * b - - 72 * y1_2 * y1_1^2 * y1_0 * a * c - 54 * y1_2 * y1_1^2 * y1_0 * b^2 - - 108 * y1_2 * y1_1^2 * y1_0 * b * c - 54 * y1_2 * y1_1^2 * y1_0 * c^2 + - 48 * y1_2 * y1_1 * y1_0^4 * b + - 48 * y1_2 * y1_1 * y1_0^4 * c - 18 * y1_2 * y1_1 * y1_0^2 * a^2 * b - - 18 * y1_2 * y1_1 * y1_0^2 * a^2 * c - 18 * y1_2 * y1_1 * y1_0^2 * a * b^2 - - 108 * y1_2 * y1_1 * y1_0^2 * a * b * c - 18 * y1_2 * y1_1 * y1_0^2 * a * c^2 - - 18 * y1_2 * y1_1 * y1_0^2 * b^3 - 126 * y1_2 * y1_1 * y1_0^2 * b^2 * c - - 126 * y1_2 * y1_1 * y1_0^2 * b * c^2 - 18 * y1_2 * y1_1 * y1_0^2 * c^3 - - 24 * y1_2 * y1_0^5 * a^2 - 48 * y1_2 * y1_0^5 * a * b - - 48 * y1_2 * y1_0^5 * a * c + - 18 * y1_2 * y1_0^3 * a^2 * b^2 + - 36 * y1_2 * y1_0^3 * a^2 * b * c + - 18 * y1_2 * y1_0^3 * a^2 * c^2 + - 18 * y1_2 * y1_0^3 * a * b^3 + - 126 * y1_2 * y1_0^3 * a * b^2 * c + - 126 * y1_2 * y1_0^3 * a * b * c^2 + - 18 * y1_2 * y1_0^3 * a * c^3 + - 9 * y1_1^4 * a^2 + - 18 * y1_1^4 * a * b + - 18 * y1_1^4 * a * c + - 9 * y1_1^4 * b^2 + - 18 * y1_1^4 * b * c + - 9 * y1_1^4 * c^2 - 24 * y1_1^3 * y1_0^3 * a - 24 * y1_1^3 * y1_0^3 * b - - 24 * y1_1^3 * y1_0^3 * c + - 18 * y1_1^3 * y1_0 * a^2 * b + - 18 * y1_1^3 * y1_0 * a^2 * c + - 36 * y1_1^3 * y1_0 * a * b^2 + - 72 * y1_1^3 * y1_0 * a * b * c + - 36 * y1_1^3 * y1_0 * a * c^2 + - 18 * y1_1^3 * y1_0 * b^3 + - 54 * y1_1^3 * y1_0 * b^2 * c + - 54 * y1_1^3 * y1_0 * b * c^2 + - 18 * y1_1^3 * y1_0 * c^3 + - 16 * y1_1^2 * y1_0^6 + - 24 * y1_1^2 * y1_0^4 * a^2 - 12 * y1_1^2 * y1_0^4 * b^2 - - 72 * y1_1^2 * y1_0^4 * b * c - 12 * y1_1^2 * y1_0^4 * c^2 - - 18 * y1_1^2 * y1_0^2 * a^2 * b^2 - 18 * y1_1^2 * y1_0^2 * a^2 * c^2 - - 18 * y1_1^2 * y1_0^2 * a * b^3 + - 18 * y1_1^2 * y1_0^2 * a * b^2 * c + - 18 * y1_1^2 * y1_0^2 * a * b * c^2 - 18 * y1_1^2 * y1_0^2 * a * c^3 + - 36 * y1_1^2 * y1_0^2 * b^3 * c + - 72 * y1_1^2 * y1_0^2 * b^2 * c^2 + - 36 * y1_1^2 * y1_0^2 * b * c^3 - 32 * y1_1 * y1_0^7 * a + - 24 * y1_1 * y1_0^5 * a^2 * b + - 24 * y1_1 * y1_0^5 * a^2 * c + - 24 * y1_1 * y1_0^5 * a * b^2 + - 144 * y1_1 * y1_0^5 * a * b * c + - 24 * y1_1 * y1_0^5 * a * c^2 - 72 * y1_1 * y1_0^3 * a^2 * b^2 * c - - 72 * y1_1 * y1_0^3 * a^2 * b * c^2 - 72 * y1_1 * y1_0^3 * a * b^3 * c - - 144 * y1_1 * y1_0^3 * a * b^2 * c^2 - 72 * y1_1 * y1_0^3 * a * b * c^3 + - 16 * y1_0^8 * a^2 - 12 * y1_0^6 * a^2 * b^2 - 72 * y1_0^6 * a^2 * b * c - - 12 * y1_0^6 * a^2 * c^2 + - 36 * y1_0^4 * a^2 * b^3 * c + - 72 * y1_0^4 * a^2 * b^2 * c^2 + - 36 * y1_0^4 * a^2 * b * c^3 + #--------------------------------------- + # Chen-Lee model + R, (y1_0, y1_1, y1_2, y1_3, a, b, c) = + polynomial_ring(QQ, ["y1(t)_0", "y1(t)_1", "y1(t)_2", "y1(t)_3", "a", "b", "c"]) + correct = + 9 * y1_3^2 * y1_0^2 - 18 * y1_3 * y1_2 * y1_1 * y1_0 - + 18 * y1_3 * y1_2 * y1_0^2 * a - 36 * y1_3 * y1_2 * y1_0^2 * b - + 36 * y1_3 * y1_2 * y1_0^2 * c + + 18 * y1_3 * y1_1^2 * y1_0 * a + + 18 * y1_3 * y1_1^2 * y1_0 * b + + 18 * y1_3 * y1_1^2 * y1_0 * c - 24 * y1_3 * y1_1 * y1_0^4 + + 18 * y1_3 * y1_1 * y1_0^2 * a * b + + 18 * y1_3 * y1_1 * y1_0^2 * a * c + + 18 * y1_3 * y1_1 * y1_0^2 * b^2 + + 36 * y1_3 * y1_1 * y1_0^2 * b * c + + 18 * y1_3 * y1_1 * y1_0^2 * c^2 + + 24 * y1_3 * y1_0^5 * a - 18 * y1_3 * y1_0^3 * a * b^2 - + 36 * y1_3 * y1_0^3 * a * b * c - 18 * y1_3 * y1_0^3 * a * c^2 + + 9 * y1_2^2 * y1_1^2 + + 18 * y1_2^2 * y1_1 * y1_0 * a + + 36 * y1_2^2 * y1_1 * y1_0 * b + + 36 * y1_2^2 * y1_1 * y1_0 * c + + 9 * y1_2^2 * y1_0^2 * a^2 + + 36 * y1_2^2 * y1_0^2 * a * b + + 36 * y1_2^2 * y1_0^2 * a * c + + 27 * y1_2^2 * y1_0^2 * b^2 + + 90 * y1_2^2 * y1_0^2 * b * c + + 27 * y1_2^2 * y1_0^2 * c^2 - 18 * y1_2 * y1_1^3 * a - 18 * y1_2 * y1_1^3 * b - + 18 * y1_2 * y1_1^3 * c + 24 * y1_2 * y1_1^2 * y1_0^3 - + 18 * y1_2 * y1_1^2 * y1_0 * a^2 - 72 * y1_2 * y1_1^2 * y1_0 * a * b - + 72 * y1_2 * y1_1^2 * y1_0 * a * c - 54 * y1_2 * y1_1^2 * y1_0 * b^2 - + 108 * y1_2 * y1_1^2 * y1_0 * b * c - 54 * y1_2 * y1_1^2 * y1_0 * c^2 + + 48 * y1_2 * y1_1 * y1_0^4 * b + + 48 * y1_2 * y1_1 * y1_0^4 * c - 18 * y1_2 * y1_1 * y1_0^2 * a^2 * b - + 18 * y1_2 * y1_1 * y1_0^2 * a^2 * c - 18 * y1_2 * y1_1 * y1_0^2 * a * b^2 - + 108 * y1_2 * y1_1 * y1_0^2 * a * b * c - 18 * y1_2 * y1_1 * y1_0^2 * a * c^2 - + 18 * y1_2 * y1_1 * y1_0^2 * b^3 - 126 * y1_2 * y1_1 * y1_0^2 * b^2 * c - + 126 * y1_2 * y1_1 * y1_0^2 * b * c^2 - 18 * y1_2 * y1_1 * y1_0^2 * c^3 - + 24 * y1_2 * y1_0^5 * a^2 - 48 * y1_2 * y1_0^5 * a * b - + 48 * y1_2 * y1_0^5 * a * c + + 18 * y1_2 * y1_0^3 * a^2 * b^2 + + 36 * y1_2 * y1_0^3 * a^2 * b * c + + 18 * y1_2 * y1_0^3 * a^2 * c^2 + + 18 * y1_2 * y1_0^3 * a * b^3 + + 126 * y1_2 * y1_0^3 * a * b^2 * c + + 126 * y1_2 * y1_0^3 * a * b * c^2 + + 18 * y1_2 * y1_0^3 * a * c^3 + + 9 * y1_1^4 * a^2 + + 18 * y1_1^4 * a * b + + 18 * y1_1^4 * a * c + + 9 * y1_1^4 * b^2 + + 18 * y1_1^4 * b * c + + 9 * y1_1^4 * c^2 - 24 * y1_1^3 * y1_0^3 * a - 24 * y1_1^3 * y1_0^3 * b - + 24 * y1_1^3 * y1_0^3 * c + + 18 * y1_1^3 * y1_0 * a^2 * b + + 18 * y1_1^3 * y1_0 * a^2 * c + + 36 * y1_1^3 * y1_0 * a * b^2 + + 72 * y1_1^3 * y1_0 * a * b * c + + 36 * y1_1^3 * y1_0 * a * c^2 + + 18 * y1_1^3 * y1_0 * b^3 + + 54 * y1_1^3 * y1_0 * b^2 * c + + 54 * y1_1^3 * y1_0 * b * c^2 + + 18 * y1_1^3 * y1_0 * c^3 + + 16 * y1_1^2 * y1_0^6 + + 24 * y1_1^2 * y1_0^4 * a^2 - 12 * y1_1^2 * y1_0^4 * b^2 - + 72 * y1_1^2 * y1_0^4 * b * c - 12 * y1_1^2 * y1_0^4 * c^2 - + 18 * y1_1^2 * y1_0^2 * a^2 * b^2 - 18 * y1_1^2 * y1_0^2 * a^2 * c^2 - + 18 * y1_1^2 * y1_0^2 * a * b^3 + + 18 * y1_1^2 * y1_0^2 * a * b^2 * c + + 18 * y1_1^2 * y1_0^2 * a * b * c^2 - 18 * y1_1^2 * y1_0^2 * a * c^3 + + 36 * y1_1^2 * y1_0^2 * b^3 * c + + 72 * y1_1^2 * y1_0^2 * b^2 * c^2 + + 36 * y1_1^2 * y1_0^2 * b * c^3 - 32 * y1_1 * y1_0^7 * a + + 24 * y1_1 * y1_0^5 * a^2 * b + + 24 * y1_1 * y1_0^5 * a^2 * c + + 24 * y1_1 * y1_0^5 * a * b^2 + + 144 * y1_1 * y1_0^5 * a * b * c + + 24 * y1_1 * y1_0^5 * a * c^2 - 72 * y1_1 * y1_0^3 * a^2 * b^2 * c - + 72 * y1_1 * y1_0^3 * a^2 * b * c^2 - 72 * y1_1 * y1_0^3 * a * b^3 * c - + 144 * y1_1 * y1_0^3 * a * b^2 * c^2 - 72 * y1_1 * y1_0^3 * a * b * c^3 + + 16 * y1_0^8 * a^2 - 12 * y1_0^6 * a^2 * b^2 - 72 * y1_0^6 * a^2 * b * c - + 12 * y1_0^6 * a^2 * c^2 + + 36 * y1_0^4 * a^2 * b^3 * c + + 72 * y1_0^4 * a^2 * b^2 * c^2 + + 36 * y1_0^4 * a^2 * b * c^3 - push!( - test_cases, - Dict( - :ode => @ODEmodel( - x0'(t) = a * x0(t) - x1(t) * x2(t), - x1'(t) = b * x1(t) + x0(t) * x2(t), - x2'(t) = c * x2(t) + 1 // 3 * x0(t) * x1(t), - y1(t) = x0(t) - ), - :correct => correct, + push!( + test_cases, + Dict( + :ode => @ODEmodel( + x0'(t) = a * x0(t) - x1(t) * x2(t), + x1'(t) = b * x1(t) + x0(t) * x2(t), + x2'(t) = c * x2(t) + 1 // 3 * x0(t) * x1(t), + y1(t) = x0(t) ), - ) + :correct => correct, + ), + ) - #--------------------------------------- + #--------------------------------------- - # predator-prey model - R, (y1_0, y1_1, y1_2, a, b, c, d) = - polynomial_ring(QQ, ["y1(t)_0", "y1(t)_1", "y1(t)_2", "a", "b", "c", "d"]) - correct = - y1_2 * y1_0 - y1_1^2 - y1_1 * y1_0^2 * d + y1_1 * y1_0 * c + y1_0^3 * a * d - - y1_0^2 * a * c + # predator-prey model + R, (y1_0, y1_1, y1_2, a, b, c, d) = + polynomial_ring(QQ, ["y1(t)_0", "y1(t)_1", "y1(t)_2", "a", "b", "c", "d"]) + correct = + y1_2 * y1_0 - y1_1^2 - y1_1 * y1_0^2 * d + y1_1 * y1_0 * c + y1_0^3 * a * d - + y1_0^2 * a * c - push!( - test_cases, - Dict( - :ode => @ODEmodel( - x0'(t) = a * x0(t) - b * x0(t) * x1(t), - x1'(t) = -c * x1(t) + d * x0(t) * x1(t), - y1(t) = x0(t) - ), - :correct => correct, + push!( + test_cases, + Dict( + :ode => @ODEmodel( + x0'(t) = a * x0(t) - b * x0(t) * x1(t), + x1'(t) = -c * x1(t) + d * x0(t) * x1(t), + y1(t) = x0(t) ), - ) + :correct => correct, + ), + ) - #--------------------------------------- + #--------------------------------------- - for case in test_cases - ode = case[:ode] - io_eq = collect(values(find_ioequations(ode)))[1] - correct = parent_ring_change(case[:correct], parent(io_eq)) - divisibility, remainder = divides(io_eq, correct) - @test divisibility - @test total_degree(remainder) == 0 - end + for case in test_cases + ode = case[:ode] + io_eq = collect(values(find_ioequations(ode)))[1] + correct = parent_ring_change(case[:correct], parent(io_eq)) + divisibility, remainder = divides(io_eq, correct) + @test divisibility + @test total_degree(remainder) == 0 end end diff --git a/test/io_projections.jl b/test/io_projections.jl index 84cde582..38f52c88 100644 --- a/test/io_projections.jl +++ b/test/io_projections.jl @@ -1,67 +1,65 @@ -if GROUP == "All" || GROUP == "Core" - @testset "IO-projections (+ extra projection)" begin - cases = [] +@testset "IO-projections (+ extra projection)" begin + cases = [] - # Example from remark 3 in https://arxiv.org/pdf/2111.00991.pdf - ode = @ODEmodel( - x1'(t) = (1 + x1(t)^2) // 2, - x2'(t) = (1 - x1(t)^2) // (1 + x1(t)^2), - y1(t) = 2 * x1(t) // (b * (1 + x1(t)^2)), - y2(t) = x2(t) - ) - push!(cases, ode) + # Example from remark 3 in https://arxiv.org/pdf/2111.00991.pdf + ode = @ODEmodel( + x1'(t) = (1 + x1(t)^2) // 2, + x2'(t) = (1 - x1(t)^2) // (1 + x1(t)^2), + y1(t) = 2 * x1(t) // (b * (1 + x1(t)^2)), + y2(t) = x2(t) + ) + push!(cases, ode) - #-------------------------------------------------------- + #-------------------------------------------------------- - # Example from https://github.com/SciML/StructuralIdentifiability.jl/issues/132 - ode = @ODEmodel( - Complex'(t) = - 1 / C * ( - ((2 * kon * free_receptor(t) * Drug(t) - koff * Complex(t)) * C) - - (ke_Complex * Complex(t) * C) - ( - (kon_2 * free_receptor(t) * Complex(t) - 2 * koff * Complex_2(t)) * C - ) - ), - Complex_2'(t) = - 1 / C * ( - ( - (kon_2 * free_receptor(t) * Complex(t) - 2 * koff * Complex_2(t)) * C - ) - (ke_Complex_2 * Complex_2(t) * C) - ), - Drug'(t) = - 1 / C * ( - -(ke_Drug * Drug(t) * C) - - ((2 * kon * free_receptor(t) * Drug(t) - koff * Complex(t)) * C) + - (45 / 100 * ka * Drug_SC(t)) - ), - free_receptor'(t) = - 1 / C * ( - -((2 * kon * free_receptor(t) * Drug(t) - koff * Complex(t)) * C) + - (66 / 2500 * C) - (kdeg_free_receptor * free_receptor(t) * C) - ( - (kon_2 * free_receptor(t) * Complex(t) - 2 * koff * Complex_2(t)) * C - ) - ), - Drug_SC'(t) = - -(45 / 100 * ka * Drug_SC(t)) - ((1 - 45 / 100) * ka * Drug_SC(t)) + - u_SC(t), - y1(t) = Drug(t), - y2(t) = free_receptor(t) + Complex(t) + 2 * Complex_2(t) - ) - push!(cases, ode) + # Example from https://github.com/SciML/StructuralIdentifiability.jl/issues/132 + ode = @ODEmodel( + Complex'(t) = + 1 / C * ( + ((2 * kon * free_receptor(t) * Drug(t) - koff * Complex(t)) * C) - + (ke_Complex * Complex(t) * C) - ( + (kon_2 * free_receptor(t) * Complex(t) - 2 * koff * Complex_2(t)) * C + ) + ), + Complex_2'(t) = + 1 / C * ( + ( + (kon_2 * free_receptor(t) * Complex(t) - 2 * koff * Complex_2(t)) * C + ) - (ke_Complex_2 * Complex_2(t) * C) + ), + Drug'(t) = + 1 / C * ( + -(ke_Drug * Drug(t) * C) - + ((2 * kon * free_receptor(t) * Drug(t) - koff * Complex(t)) * C) + + (45 / 100 * ka * Drug_SC(t)) + ), + free_receptor'(t) = + 1 / C * ( + -((2 * kon * free_receptor(t) * Drug(t) - koff * Complex(t)) * C) + + (66 / 2500 * C) - (kdeg_free_receptor * free_receptor(t) * C) - ( + (kon_2 * free_receptor(t) * Complex(t) - 2 * koff * Complex_2(t)) * C + ) + ), + Drug_SC'(t) = + -(45 / 100 * ka * Drug_SC(t)) - ((1 - 45 / 100) * ka * Drug_SC(t)) + + u_SC(t), + y1(t) = Drug(t), + y2(t) = free_receptor(t) + Complex(t) + 2 * Complex_2(t) + ) + push!(cases, ode) - #--------------------------------------------------------- + #--------------------------------------------------------- - for ode in cases - proj, gpg, _ = find_ioprojections(ode, false) - for p in values(proj) - @test choose([p], gpg) == p - end - @test !check_primality(proj) - # taking simply a sum instead of random linear combination - extra_projection = sum(keys(proj)) - proj, gpg, projection_poly = find_ioprojections(ode, false, extra_projection) - @test choose([projection_poly], gpg) == projection_poly - @test check_primality(proj, [projection_poly]) + for ode in cases + proj, gpg, _ = find_ioprojections(ode, false) + for p in values(proj) + @test choose([p], gpg) == p end + @test !check_primality(proj) + # taking simply a sum instead of random linear combination + extra_projection = sum(keys(proj)) + proj, gpg, projection_poly = find_ioprojections(ode, false, extra_projection) + @test choose([projection_poly], gpg) == projection_poly + @test check_primality(proj, [projection_poly]) end end diff --git a/test/lc_univariate.jl b/test/lc_univariate.jl index 532b2a50..08c2922c 100644 --- a/test/lc_univariate.jl +++ b/test/lc_univariate.jl @@ -1,10 +1,8 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Univariate leading coefficient" begin - R, (x, y, z) = Nemo.polynomial_ring(Nemo.QQ, ["x", "y", "z"]) - p = x^2 * y + x^2 * (z + z^3) + y - 5 - @test lc_univariate(p, x) == y + z + z^3 - @test lc_univariate(p, z) == x^2 - @test lc_univariate(p, y) == 1 + x^2 - @test lc_univariate(x + y, z) == x + y - end +@testset "Univariate leading coefficient" begin + R, (x, y, z) = Nemo.polynomial_ring(Nemo.QQ, ["x", "y", "z"]) + p = x^2 * y + x^2 * (z + z^3) + y - 5 + @test lc_univariate(p, x) == y + z + z^3 + @test lc_univariate(p, z) == x^2 + @test lc_univariate(p, y) == 1 + x^2 + @test lc_univariate(x + y, z) == x + y end diff --git a/test/lie_derivative.jl b/test/lie_derivative.jl index 5969405f..250b22d7 100644 --- a/test/lie_derivative.jl +++ b/test/lie_derivative.jl @@ -1,24 +1,22 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Lie derivative" begin - ode = @ODEmodel( - x1'(t) = a * x1(t) + b * u(t), - x2'(t) = 1 // (1 - x1(t) - x2(t)), - y(t) = x1(t) - ) - @test lie_derivative(x1 + x2, ode) == - ((a * x1 + b * u) * (1 - x1 - x2) + 1) // (1 - x1 - x2) - @test lie_derivative(one(x1), ode) == zero(x1) - @test lie_derivative(zero(x1), ode) == zero(x1) +@testset "Lie derivative" begin + ode = @ODEmodel( + x1'(t) = a * x1(t) + b * u(t), + x2'(t) = 1 // (1 - x1(t) - x2(t)), + y(t) = x1(t) + ) + @test lie_derivative(x1 + x2, ode) == + ((a * x1 + b * u) * (1 - x1 - x2) + 1) // (1 - x1 - x2) + @test lie_derivative(one(x1), ode) == zero(x1) + @test lie_derivative(zero(x1), ode) == zero(x1) - ode = @ODEmodel( - x1'(t) = x2(t) // x1(t) + x2(t) * u(t), - x2'(t) = -1 - x1(t) * u(t), - y(t) = 1 - ) - @test lie_derivative(x1^2 + x2^2 // 1, ode) == zero(parent(ode)) // 1 + ode = @ODEmodel( + x1'(t) = x2(t) // x1(t) + x2(t) * u(t), + x2'(t) = -1 - x1(t) * u(t), + y(t) = 1 + ) + @test lie_derivative(x1^2 + x2^2 // 1, ode) == zero(parent(ode)) // 1 - ode = @ODEmodel(x1'(t) = 2x1(t) + 3a, y(t) = x1(t)) - @test lie_derivative(5x1^2, ode) == 10x1 * (2x1 + 3a) - @test lie_derivative(a, ode) == zero(a) - end + ode = @ODEmodel(x1'(t) = 2x1(t) + 3a, y(t) = x1(t)) + @test lie_derivative(5x1^2, ode) == 10x1 * (2x1 + 3a) + @test lie_derivative(a, ode) == zero(a) end diff --git a/test/linear_compartment.jl b/test/linear_compartment.jl index 1b2e48bd..d25ca9f1 100644 --- a/test/linear_compartment.jl +++ b/test/linear_compartment.jl @@ -1,169 +1,167 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Identifiability of linear compartment models" begin - test_cases = [] - - push!( - test_cases, - Dict( - :graph => [Array{Int, 1}(), [1], [1]], - :outputs => [1], - :leaks => Array{Int, 1}(), - :inputs => [1], - :result => Dict((3, 1) => :locally, (2, 1) => :locally), +@testset "Identifiability of linear compartment models" begin + test_cases = [] + + push!( + test_cases, + Dict( + :graph => [Array{Int, 1}(), [1], [1]], + :outputs => [1], + :leaks => Array{Int, 1}(), + :inputs => [1], + :result => Dict((3, 1) => :locally, (2, 1) => :locally), + ), + ) + + #-------------------------------------------------------------------------- + + push!( + test_cases, + Dict( + :graph => [Array{Int, 1}(), [1], [1, 2]], + :outputs => [1], + :leaks => Array{Int, 1}(), + :inputs => [1], + :result => Dict( + (3, 1) => :nonidentifiable, + (3, 2) => :nonidentifiable, + (2, 1) => :locally, ), - ) - - #-------------------------------------------------------------------------- - - push!( - test_cases, - Dict( - :graph => [Array{Int, 1}(), [1], [1, 2]], - :outputs => [1], - :leaks => Array{Int, 1}(), - :inputs => [1], - :result => Dict( - (3, 1) => :nonidentifiable, - (3, 2) => :nonidentifiable, - (2, 1) => :locally, - ), + ), + ) + + #-------------------------------------------------------------------------- + + push!( + test_cases, + Dict( + :graph => [[2], [1, 3], [1, 2]], + :outputs => [1], + :leaks => Array{Int, 1}(), + :inputs => [2], + :result => Dict( + (2, 3) => :nonidentifiable, + (3, 1) => :nonidentifiable, + (1, 2) => :locally, + (3, 2) => :nonidentifiable, + (2, 1) => :globally, ), - ) - - #-------------------------------------------------------------------------- - - push!( - test_cases, - Dict( - :graph => [[2], [1, 3], [1, 2]], - :outputs => [1], - :leaks => Array{Int, 1}(), - :inputs => [2], - :result => Dict( - (2, 3) => :nonidentifiable, - (3, 1) => :nonidentifiable, - (1, 2) => :locally, - (3, 2) => :nonidentifiable, - (2, 1) => :globally, - ), + ), + ) + + #-------------------------------------------------------------------------- + + push!( + test_cases, + Dict( + :graph => [[2], [1, 3], [1, 2]], + :outputs => [2], + :leaks => Array{Int, 1}(), + :inputs => [2], + :result => Dict( + (2, 3) => :nonidentifiable, + (3, 1) => :nonidentifiable, + (1, 2) => :locally, + (3, 2) => :nonidentifiable, + (2, 1) => :nonidentifiable, ), - ) - - #-------------------------------------------------------------------------- - - push!( - test_cases, - Dict( - :graph => [[2], [1, 3], [1, 2]], - :outputs => [2], - :leaks => Array{Int, 1}(), - :inputs => [2], - :result => Dict( - (2, 3) => :nonidentifiable, - (3, 1) => :nonidentifiable, - (1, 2) => :locally, - (3, 2) => :nonidentifiable, - (2, 1) => :nonidentifiable, - ), + ), + ) + + #-------------------------------------------------------------------------- + + push!( + test_cases, + Dict( + :graph => [[2, 3], [1, 3], [1, 2]], + :outputs => [3], + :leaks => [1], + :inputs => [1, 2], + :result => Dict( + (1, 2) => :globally, + (1, 3) => :globally, + (2, 1) => :globally, + (2, 3) => :globally, + (3, 1) => :globally, + (3, 2) => :globally, + (1, 0) => :globally, ), - ) - - #-------------------------------------------------------------------------- - - push!( - test_cases, - Dict( - :graph => [[2, 3], [1, 3], [1, 2]], - :outputs => [3], - :leaks => [1], - :inputs => [1, 2], - :result => Dict( - (1, 2) => :globally, - (1, 3) => :globally, - (2, 1) => :globally, - (2, 3) => :globally, - (3, 1) => :globally, - (3, 2) => :globally, - (1, 0) => :globally, - ), + ), + ) + + #-------------------------------------------------------------------------- + + push!( + test_cases, + Dict( + :graph => [[2, 3], [1, 3], [1, 2]], + :outputs => [3], + :leaks => Array{Int, 1}(), + :inputs => [1, 2], + :result => Dict( + (1, 2) => :nonidentifiable, + (1, 3) => :globally, + (2, 1) => :nonidentifiable, + (2, 3) => :globally, + (3, 1) => :nonidentifiable, + (3, 2) => :nonidentifiable, ), - ) - - #-------------------------------------------------------------------------- - - push!( - test_cases, - Dict( - :graph => [[2, 3], [1, 3], [1, 2]], - :outputs => [3], - :leaks => Array{Int, 1}(), - :inputs => [1, 2], - :result => Dict( - (1, 2) => :nonidentifiable, - (1, 3) => :globally, - (2, 1) => :nonidentifiable, - (2, 3) => :globally, - (3, 1) => :nonidentifiable, - (3, 2) => :nonidentifiable, - ), + ), + ) + + #-------------------------------------------------------------------------- + + push!( + test_cases, + Dict( + :graph => [[2], [1, 3], [1, 2]], + :outputs => [3], + :leaks => [1], + :inputs => [1, 3], + :result => Dict( + (1, 2) => :globally, + (2, 1) => :globally, + (2, 3) => :globally, + (3, 1) => :globally, + (3, 2) => :globally, + (1, 0) => :globally, ), - ) - - #-------------------------------------------------------------------------- - - push!( - test_cases, - Dict( - :graph => [[2], [1, 3], [1, 2]], - :outputs => [3], - :leaks => [1], - :inputs => [1, 3], - :result => Dict( - (1, 2) => :globally, - (2, 1) => :globally, - (2, 3) => :globally, - (3, 1) => :globally, - (3, 2) => :globally, - (1, 0) => :globally, - ), + ), + ) + + #-------------------------------------------------------------------------- + + push!( + test_cases, + Dict( + :graph => [[2], [1, 3], [1, 2]], + :outputs => [3], + :leaks => Array{Int, 1}(), + :inputs => [1, 3], + :result => Dict( + (1, 2) => :nonidentifiable, + (2, 1) => :nonidentifiable, + (2, 3) => :nonidentifiable, + (3, 1) => :nonidentifiable, + (3, 2) => :nonidentifiable, ), - ) + ), + ) - #-------------------------------------------------------------------------- - - push!( - test_cases, - Dict( - :graph => [[2], [1, 3], [1, 2]], - :outputs => [3], - :leaks => Array{Int, 1}(), - :inputs => [1, 3], - :result => Dict( - (1, 2) => :nonidentifiable, - (2, 1) => :nonidentifiable, - (2, 3) => :nonidentifiable, - (3, 1) => :nonidentifiable, - (3, 2) => :nonidentifiable, - ), - ), - ) + #-------------------------------------------------------------------------- - #-------------------------------------------------------------------------- - - for case in test_cases - ode = linear_compartment_model( - case[:graph], - case[:inputs], - case[:outputs], - case[:leaks], - ) - bring = ode.poly_ring - correct = Dict{QQMPolyRingElem, Symbol}() - for (e, id) in case[:result] - correct[str_to_var("a_$(e[2])_$(e[1])", bring)] = id - end - result = assess_identifiability(ode, funcs_to_check = collect(keys(correct))) - @test correct == result + for case in test_cases + ode = linear_compartment_model( + case[:graph], + case[:inputs], + case[:outputs], + case[:leaks], + ) + bring = ode.poly_ring + correct = Dict{QQMPolyRingElem, Symbol}() + for (e, id) in case[:result] + correct[str_to_var("a_$(e[2])_$(e[1])", bring)] = id end + result = assess_identifiability(ode, funcs_to_check = collect(keys(correct))) + @test correct == result end end diff --git a/test/local_identifiability.jl b/test/local_identifiability.jl index 166284c8..0601baea 100644 --- a/test/local_identifiability.jl +++ b/test/local_identifiability.jl @@ -1,135 +1,133 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Assessing local identifiability" begin - test_cases = [] +@testset "Assessing local identifiability" begin + test_cases = [] - # 2-compartiment model + # 2-compartiment model - ode = @ODEmodel( - x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t), - x1'(t) = a21 * x0(t) - a12 * x1(t), - y(t) = x0(t) - ) - funcs_to_test = [ - a01, - a21, - a12, - a01 * a12, - a01 + a12 + a21, - (a01 + a12 + a21) // (a01 * a12), - x0, - x1, - ] - correct = [false, false, false, true, true, true, true, false] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) + ode = @ODEmodel( + x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t), + x1'(t) = a21 * x0(t) - a12 * x1(t), + y(t) = x0(t) + ) + funcs_to_test = [ + a01, + a21, + a12, + a01 * a12, + a01 + a12 + a21, + (a01 + a12 + a21) // (a01 * a12), + x0, + x1, + ] + correct = [false, false, false, true, true, true, true, false] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) - #-------------------------------------------------------------------------- + #-------------------------------------------------------------------------- - ode = @ODEmodel( - x0'(t) = a * x0(t) - b * x0(t) * x1(t) + u(t), - x1'(t) = c * x1(t) + d * x0(t) * x1(t), - y(t) = x0(t) - ) - funcs_to_test = [a, b, c, d, x0, x1] - correct = [true, false, true, true, true, false] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) + ode = @ODEmodel( + x0'(t) = a * x0(t) - b * x0(t) * x1(t) + u(t), + x1'(t) = c * x1(t) + d * x0(t) * x1(t), + y(t) = x0(t) + ) + funcs_to_test = [a, b, c, d, x0, x1] + correct = [true, false, true, true, true, false] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) - #-------------------------------------------------------------------------- + #-------------------------------------------------------------------------- - ode = @ODEmodel( - S'(t) = mu - bi * S(t) * I(t) - bw * S(t) * W(t) - mu * S(t) + a * R(t), - I'(t) = bw * S(t) * W(t) + bi * S(t) * I(t) - (gam + mu) * I(t), - W'(t) = xi * (I(t) - W(t)), - R'(t) = gam * I(t) - (mu + a) * R(t), - y(t) = k * I(t) - ) - funcs_to_test = [mu, bi, bw, a, xi, gam, mu, gam + mu, k, S, I, W, R] - correct = [true for _ in funcs_to_test] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => OrderedDict(funcs_to_test .=> correct), - ), - ) + ode = @ODEmodel( + S'(t) = mu - bi * S(t) * I(t) - bw * S(t) * W(t) - mu * S(t) + a * R(t), + I'(t) = bw * S(t) * W(t) + bi * S(t) * I(t) - (gam + mu) * I(t), + W'(t) = xi * (I(t) - W(t)), + R'(t) = gam * I(t) - (mu + a) * R(t), + y(t) = k * I(t) + ) + funcs_to_test = [mu, bi, bw, a, xi, gam, mu, gam + mu, k, S, I, W, R] + correct = [true for _ in funcs_to_test] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => OrderedDict(funcs_to_test .=> correct), + ), + ) - #-------------------------------------------------------------------------- + #-------------------------------------------------------------------------- - ode = @ODEmodel( - x1'(t) = -b * x1(t) + 1 / (c + x4(t)), - x2'(t) = alpha * x1(t) - beta * x2(t), - x3'(t) = gama * x2(t) - delta * x3(t), - x4'(t) = sigma * x4(t) * (gama * x2(t) - delta * x3(t)) / x3(t), - y(t) = x1(t) - ) - funcs_to_test = [b, c, alpha, beta, delta, gama, beta + delta, beta * delta] - correct = OrderedDict([ - b => true, - c => true, - alpha => false, - beta => true, - delta => true, - gama => false, - beta + delta => true, - beta * delta => true, - ]) - push!(test_cases, Dict(:ode => ode, :funcs => funcs_to_test, :correct => correct)) + ode = @ODEmodel( + x1'(t) = -b * x1(t) + 1 / (c + x4(t)), + x2'(t) = alpha * x1(t) - beta * x2(t), + x3'(t) = gama * x2(t) - delta * x3(t), + x4'(t) = sigma * x4(t) * (gama * x2(t) - delta * x3(t)) / x3(t), + y(t) = x1(t) + ) + funcs_to_test = [b, c, alpha, beta, delta, gama, beta + delta, beta * delta] + correct = OrderedDict([ + b => true, + c => true, + alpha => false, + beta => true, + delta => true, + gama => false, + beta + delta => true, + beta * delta => true, + ]) + push!(test_cases, Dict(:ode => ode, :funcs => funcs_to_test, :correct => correct)) - #-------------------------------------------------------------------------- + #-------------------------------------------------------------------------- - ode = @ODEmodel( - x1'(t) = a1 + a2 + a3 * a4, - x2'(t) = a4 * a5 + a6 + a7 - 8 * a8, - y(t) = x1(t) * x2(t) - ) - funcs_to_test = [a1, a2, a3, a4, a5, a6, a7, a8] - correct = OrderedDict(a => false for a in funcs_to_test) - push!(test_cases, Dict(:ode => ode, :funcs => funcs_to_test, :correct => correct)) + ode = @ODEmodel( + x1'(t) = a1 + a2 + a3 * a4, + x2'(t) = a4 * a5 + a6 + a7 - 8 * a8, + y(t) = x1(t) * x2(t) + ) + funcs_to_test = [a1, a2, a3, a4, a5, a6, a7, a8] + correct = OrderedDict(a => false for a in funcs_to_test) + push!(test_cases, Dict(:ode => ode, :funcs => funcs_to_test, :correct => correct)) - #-------------------------------------------------------------------------- + #-------------------------------------------------------------------------- - ode = @ODEmodel( - x1'(t) = 0, - x2'(t) = 0, - x3'(t) = x3(t), - y(t) = (x1(t) + x2(t))^2, - y2(t) = x3(t) - ) - funcs_to_test = [x1, x2, x1 + x2] - correct = OrderedDict(x1 => false, x2 => false, x1 + x2 => true) - push!(test_cases, Dict(:ode => ode, :funcs => funcs_to_test, :correct => correct)) + ode = @ODEmodel( + x1'(t) = 0, + x2'(t) = 0, + x3'(t) = x3(t), + y(t) = (x1(t) + x2(t))^2, + y2(t) = x3(t) + ) + funcs_to_test = [x1, x2, x1 + x2] + correct = OrderedDict(x1 => false, x2 => false, x1 + x2 => true) + push!(test_cases, Dict(:ode => ode, :funcs => funcs_to_test, :correct => correct)) - #-------------------------------------------------------------------------- + #-------------------------------------------------------------------------- - for case in test_cases - trbasis = [] - ode = case[:ode] - result = assess_local_identifiability( - ode, - funcs_to_check = case[:funcs], - trbasis = trbasis, - ) - @test result == case[:correct] - for (i, p) in enumerate(trbasis) - res_for_p = assess_local_identifiability(ode, funcs_to_check = [p]) - @test !first(values(res_for_p)) - ode = add_outputs(ode, Dict("YYY$i" => p)) - end - @test all(values(assess_local_identifiability(ode))) + for case in test_cases + trbasis = [] + ode = case[:ode] + result = assess_local_identifiability( + ode, + funcs_to_check = case[:funcs], + trbasis = trbasis, + ) + @test result == case[:correct] + for (i, p) in enumerate(trbasis) + res_for_p = assess_local_identifiability(ode, funcs_to_check = [p]) + @test !first(values(res_for_p)) + ode = add_outputs(ode, Dict("YYY$i" => p)) end + @test all(values(assess_local_identifiability(ode))) end end diff --git a/test/local_identifiability_discrete.jl b/test/local_identifiability_discrete.jl index 9e693684..5ae18f4f 100644 --- a/test/local_identifiability_discrete.jl +++ b/test/local_identifiability_discrete.jl @@ -1,65 +1,63 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Discrete local identifiability, @DDSmodel interface" begin - cases = [] - - dds = @DDSmodel(a(t + 1) = (b + c) * a(t) + 1, y(t) = a(t)) - - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict(a => true, b => false, c => false, b + c => true), - :known => :none, - ), - ) - - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict(a => true, b => false, c => false, b + c => true), - :known => :all, +@testset "Discrete local identifiability, @DDSmodel interface" begin + cases = [] + + dds = @DDSmodel(a(t + 1) = (b + c) * a(t) + 1, y(t) = a(t)) + + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict(a => true, b => false, c => false, b + c => true), + :known => :none, + ), + ) + + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict(a => true, b => false, c => false, b + c => true), + :known => :all, + ), + ) + + #--------------------- + + cases = [] + + dds = @DDSmodel(x1(t + 1) = a * x1(t) * x2(t), x2(t + 1) = b * x1(t), y(t) = x2(t)) + + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict( + a => true, + b => false, + x1 => false, + x2 => true, + b * x1 => true, ), - ) - - #--------------------- - - cases = [] - - dds = @DDSmodel(x1(t + 1) = a * x1(t) * x2(t), x2(t + 1) = b * x1(t), y(t) = x2(t)) - - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict( - a => true, - b => false, - x1 => false, - x2 => true, - b * x1 => true, - ), - :known => :none, - ), - ) - - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict(a => true, b => true, x1 => true, x2 => true), - :known => :all, - ), - ) - - #--------------------- - - for c in cases - @test assess_local_identifiability( - c[:dds]; - funcs_to_check = collect(keys(c[:res])), - known_ic = c[:known], - ) == c[:res] - end + :known => :none, + ), + ) + + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict(a => true, b => true, x1 => true, x2 => true), + :known => :all, + ), + ) + + #--------------------- + + for c in cases + @test assess_local_identifiability( + c[:dds]; + funcs_to_check = collect(keys(c[:res])), + known_ic = c[:known], + ) == c[:res] end end diff --git a/test/local_identifiability_discrete_aux.jl b/test/local_identifiability_discrete_aux.jl index 0c41ad9d..ac7852be 100644 --- a/test/local_identifiability_discrete_aux.jl +++ b/test/local_identifiability_discrete_aux.jl @@ -1,87 +1,85 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Discrete local identifiability, internal function" begin - cases = [] +@testset "Discrete local identifiability, internal function" begin + cases = [] - dds = @DDSmodel(a(t + 1) = (b + c) * a(t) + 1, y(t) = a(t)) + dds = @DDSmodel(a(t + 1) = (b + c) * a(t) + 1, y(t) = a(t)) - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict(a => true, b => false, c => false, b + c => true), - :known => :none, - ), - ) + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict(a => true, b => false, c => false, b + c => true), + :known => :none, + ), + ) - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict(a => true, b => false, c => false, b + c => true), - :known => :all, - ), - ) + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict(a => true, b => false, c => false, b + c => true), + :known => :all, + ), + ) - #--------------------- + #--------------------- - dds = @DDSmodel(a(t + 1) = b(t) * a(t) + c, b(t + 1) = d * a(t), y(t) = b(t)) + dds = @DDSmodel(a(t + 1) = b(t) * a(t) + c, b(t + 1) = d * a(t), y(t) = b(t)) - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict( - b => true, - a => false, - c => false, - d => false, - d * a => true, - d * c => true, - ), - :known => :none, + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict( + b => true, + a => false, + c => false, + d => false, + d * a => true, + d * c => true, ), - ) + :known => :none, + ), + ) - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict(b => true, a => true, c => true, d => true), - :known => [a], - ), - ) + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict(b => true, a => true, c => true, d => true), + :known => [a], + ), + ) - push!( - cases, - Dict( - :dds => dds, - :res => OrderedDict(b => true, a => true, c => true, d => true), - :known => :all, - ), - ) + push!( + cases, + Dict( + :dds => dds, + :res => OrderedDict(b => true, a => true, c => true, d => true), + :known => :all, + ), + ) - # ------------------- + # ------------------- - # Example 4 from https://doi.org/10.1016/j.automatica.2016.01.054 - dds = @DDSmodel(x(t + 1) = theta^3 * x(t), y(t) = x(t)) + # Example 4 from https://doi.org/10.1016/j.automatica.2016.01.054 + dds = @DDSmodel(x(t + 1) = theta^3 * x(t), y(t) = x(t)) - push!( - cases, - OrderedDict( - :dds => dds, - :res => Dict(theta => true, x => true), - :known => :none, - ), - ) + push!( + cases, + OrderedDict( + :dds => dds, + :res => Dict(theta => true, x => true), + :known => :none, + ), + ) - # ------------------- + # ------------------- - for c in cases - @test _assess_local_identifiability_discrete_aux( - c[:dds], - collect(keys(c[:res])), - c[:known], - ) == c[:res] - end + for c in cases + @test _assess_local_identifiability_discrete_aux( + c[:dds], + collect(keys(c[:res])), + c[:known], + ) == c[:res] end end diff --git a/test/local_identifiability_me.jl b/test/local_identifiability_me.jl index e14eb2f4..4f866ef2 100644 --- a/test/local_identifiability_me.jl +++ b/test/local_identifiability_me.jl @@ -1,233 +1,231 @@ -if GROUP == "All" || GROUP == "Core" - # Copyright (c) 2021, R. Dong, C. Goodbarke, H. Harrington, G. Pogudin - # Copyright (c) 2020, A. Ovchinnikov, A. Pillay, G. Pogudin, T. Scanlon - - # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - function _linear_compartment_model(graph, sinks) - """ - Input: - - graph - graph of the model network represented via adjacency lists - - sinks - the indices of nodes having a sink - Output: the corresponding ODE object where each parameter a_ij is replaced - with a_ij + b_ij * x_0, where x_0 is a constant input encoded as a constant output - """ - n = length(graph) - x_vars_names = ["x$i" for i in 0:n] - edges_vars_names = Array{String, 1}() - for i in 1:n - for j in graph[i] - push!(edges_vars_names, "a_$(i)_$(j)") - push!(edges_vars_names, "b_$(i)_$(j)") - end - end - for s in sinks - push!(edges_vars_names, "a_$(s)_0") - push!(edges_vars_names, "b_$(s)_0") +# Copyright (c) 2021, R. Dong, C. Goodbarke, H. Harrington, G. Pogudin +# Copyright (c) 2020, A. Ovchinnikov, A. Pillay, G. Pogudin, T. Scanlon + +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +function _linear_compartment_model(graph, sinks) + """ + Input: + - graph - graph of the model network represented via adjacency lists + - sinks - the indices of nodes having a sink + Output: the corresponding ODE object where each parameter a_ij is replaced + with a_ij + b_ij * x_0, where x_0 is a constant input encoded as a constant output + """ + n = length(graph) + x_vars_names = ["x$i" for i in 0:n] + edges_vars_names = Array{String, 1}() + for i in 1:n + for j in graph[i] + push!(edges_vars_names, "a_$(i)_$(j)") + push!(edges_vars_names, "b_$(i)_$(j)") end - R, vars = Nemo.polynomial_ring( - Nemo.QQ, - vcat(x_vars_names, edges_vars_names, ["y1", "y2"]), + end + for s in sinks + push!(edges_vars_names, "a_$(s)_0") + push!(edges_vars_names, "b_$(s)_0") + end + R, vars = Nemo.polynomial_ring( + Nemo.QQ, + vcat(x_vars_names, edges_vars_names, ["y1", "y2"]), + ) + x_vars = vars[2:(n + 1)] + x0 = vars[1] + equations = + Dict{QQMPolyRingElem, Union{QQMPolyRingElem, Generic.Frac{QQMPolyRingElem}}}( + x => R(0) for x in x_vars ) - x_vars = vars[2:(n + 1)] - x0 = vars[1] - equations = - Dict{QQMPolyRingElem, Union{QQMPolyRingElem, Generic.Frac{QQMPolyRingElem}}}( - x => R(0) for x in x_vars - ) - equations[x0] = R(0) - for i in 1:n - for j in graph[i] - rate = str_to_var("a_$(i)_$(j)", R) + str_to_var("b_$(i)_$(j)", R) * x0 - - if i != j - equations[x_vars[j]] += x_vars[i] * rate - equations[x_vars[i]] -= x_vars[i] * rate - else - equations[x_vars[i]] -= x_vars[i] * rate - end - end - if i in sinks - rate = str_to_var("a_$(i)_0", R) + str_to_var("b_$(i)_0", R) * x0 - equations[x_vars[i]] += -x_vars[i] * rate + equations[x0] = R(0) + for i in 1:n + for j in graph[i] + rate = str_to_var("a_$(i)_$(j)", R) + str_to_var("b_$(i)_$(j)", R) * x0 + + if i != j + equations[x_vars[j]] += x_vars[i] * rate + equations[x_vars[i]] -= x_vars[i] * rate + else + equations[x_vars[i]] -= x_vars[i] * rate end end - return ODE{QQMPolyRingElem}( - equations, - Dict(vars[end] => x_vars[1], vars[end - 1] => x0), - Array{QQMPolyRingElem, 1}(), - ) + if i in sinks + rate = str_to_var("a_$(i)_0", R) + str_to_var("b_$(i)_0", R) * x0 + equations[x_vars[i]] += -x_vars[i] * rate + end end + return ODE{QQMPolyRingElem}( + equations, + Dict(vars[end] => x_vars[1], vars[end - 1] => x0), + Array{QQMPolyRingElem, 1}(), + ) +end - #------------------------------------------------------------------------------ - - function bicycle(n) - """ - Generates a bidirected cycle of length n - """ - graph = [] - for i in 1:n - prev = (i == 1) ? n : (i - 1) - next = (i == n) ? 1 : i + 1 - push!(graph, [prev, next]) - end - return graph +#------------------------------------------------------------------------------ + +function bicycle(n) + """ + Generates a bidirected cycle of length n + """ + graph = [] + for i in 1:n + prev = (i == 1) ? n : (i - 1) + next = (i == n) ? 1 : i + 1 + push!(graph, [prev, next]) end + return graph +end - #------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ - function cycle(n) - """ - Single directed cycle - """ - graph = [[(i == n) ? 1 : (i + 1)] for i in 1:n] - return graph - end +function cycle(n) + """ + Single directed cycle + """ + graph = [[(i == n) ? 1 : (i + 1)] for i in 1:n] + return graph +end - #------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ - function catenary(n) - """ - Bidirected chain from 1 to n - """ - graph = [[] for i in 1:n] - for i in 1:n - if i != 1 - push!(graph[i], i - 1) - end - if i != n - push!(graph[i], i + 1) - end +function catenary(n) + """ + Bidirected chain from 1 to n + """ + graph = [[] for i in 1:n] + for i in 1:n + if i != 1 + push!(graph[i], i - 1) + end + if i != n + push!(graph[i], i + 1) end - return graph end + return graph +end - #------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ - function mammilary(n) - """ - Bidirected 'star' with center at 1 and rays to 2, ..., n - """ - graph = [] - push!(graph, [i for i in 2:n]) - for i in 2:n - push!(graph, [1]) - end - return graph +function mammilary(n) + """ + Bidirected 'star' with center at 1 and rays to 2, ..., n + """ + graph = [] + push!(graph, [i for i in 2:n]) + for i in 2:n + push!(graph, [1]) end + return graph +end - #------------------------------------------------------------------------------ - - ############################################################################### - - @testset "Assessing local identifiability (multiexperiment)" begin - - # checking bounds - - test_cases = [ - Dict(:name => "Cyclic", :graph => cycle, :bound => [3]), - Dict(:name => "Catenary", :graph => catenary, :bound => [4, 5]), - Dict(:name => "Mammilary", :graph => mammilary, :bound => [4, 5]), - ] - - n_min = 3 - n_max = 8 - for case in test_cases - for n in n_min:n_max - model = _linear_compartment_model(case[:graph](n), [1]) - println(case[:name] * ", n = $n") - @time result = assess_local_identifiability(model, type = :ME) - correct = undef - if n - n_min + 1 > length(case[:bound]) - correct = case[:bound][end] - else - correct = case[:bound][n - n_min + 1] - end - @test correct == result[2] - end - end - - # checking bounds and results +#------------------------------------------------------------------------------ - test_cases = [] +############################################################################### - ode = @ODEmodel( - x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t), - x1'(t) = a21 * x0(t) - a12 * x1(t), - y(t) = x0(t) - ) - funcs_to_test = - [a01, a21, a12, a01 * a12, a01 + a12 + a21, (a01 + a12 + a21) // (a01 * a12)] - correct = [false, false, false, true, true, true] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => (OrderedDict(funcs_to_test .=> correct), 1), - ), - ) +@testset "Assessing local identifiability (multiexperiment)" begin - #-------------------------------------------------------------------------- - - # Example 7.7 from https://arxiv.org/pdf/2011.10868.pdf - ode = - @ODEmodel(x0'(t) = 0, x1'(t) = x0(t) * x1(t) + mu1 * x0(t) + mu2, y(t) = x1(t)) - funcs_to_test = [mu1, mu2] - correct = [true, true] - push!( - test_cases, - Dict( - :ode => ode, - :funcs => funcs_to_test, - :correct => (OrderedDict(funcs_to_test .=> correct), 2), - ), - ) + # checking bounds - #-------------------------------------------------------------------------- - - # Example 7.8 from https://arxiv.org/pdf/2011.10868.pdf - ode = @ODEmodel( - S'(t) = -b * S(t) * I(t) / N, - E'(t) = b * S(t) * I(t) / N - nu * E(t), - I'(t) = nu * E(t) - a * I(t), - c'(t) = 0, - y1(t) = c(t) * I(t) + d * E(t), - y2(t) = c(t), - y3(t) = N - ) - funcs_to_test = [b, nu, d, a] - correct = OrderedDict([b => true, nu => true, d => true, a => true]) - push!( - test_cases, - Dict(:ode => ode, :funcs => funcs_to_test, :correct => (correct, 1)), - ) + test_cases = [ + Dict(:name => "Cyclic", :graph => cycle, :bound => [3]), + Dict(:name => "Catenary", :graph => catenary, :bound => [4, 5]), + Dict(:name => "Mammilary", :graph => mammilary, :bound => [4, 5]), + ] - #-------------------------------------------------------------------------- + n_min = 3 + n_max = 8 + for case in test_cases + for n in n_min:n_max + model = _linear_compartment_model(case[:graph](n), [1]) + println(case[:name] * ", n = $n") + @time result = assess_local_identifiability(model, type = :ME) + correct = undef + if n - n_min + 1 > length(case[:bound]) + correct = case[:bound][end] + else + correct = case[:bound][n - n_min + 1] + end + @test correct == result[2] + end + end - # example with 0 replicas required - ode = @ODEmodel(x'(t) = a * z(t), z'(t) = a * z(t)^2, y(t) = x(t)) - funcs_to_test = [a] - correct = OrderedDict([a => false]) - push!( - test_cases, - Dict(:ode => ode, :funcs => funcs_to_test, :correct => (correct, 0)), + # checking bounds and results + + test_cases = [] + + ode = @ODEmodel( + x0'(t) = -(a01 + a21) * x0(t) + a12 * x1(t), + x1'(t) = a21 * x0(t) - a12 * x1(t), + y(t) = x0(t) + ) + funcs_to_test = + [a01, a21, a12, a01 * a12, a01 + a12 + a21, (a01 + a12 + a21) // (a01 * a12)] + correct = [false, false, false, true, true, true] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => (OrderedDict(funcs_to_test .=> correct), 1), + ), + ) + + #-------------------------------------------------------------------------- + + # Example 7.7 from https://arxiv.org/pdf/2011.10868.pdf + ode = + @ODEmodel(x0'(t) = 0, x1'(t) = x0(t) * x1(t) + mu1 * x0(t) + mu2, y(t) = x1(t)) + funcs_to_test = [mu1, mu2] + correct = [true, true] + push!( + test_cases, + Dict( + :ode => ode, + :funcs => funcs_to_test, + :correct => (OrderedDict(funcs_to_test .=> correct), 2), + ), + ) + + #-------------------------------------------------------------------------- + + # Example 7.8 from https://arxiv.org/pdf/2011.10868.pdf + ode = @ODEmodel( + S'(t) = -b * S(t) * I(t) / N, + E'(t) = b * S(t) * I(t) / N - nu * E(t), + I'(t) = nu * E(t) - a * I(t), + c'(t) = 0, + y1(t) = c(t) * I(t) + d * E(t), + y2(t) = c(t), + y3(t) = N + ) + funcs_to_test = [b, nu, d, a] + correct = OrderedDict([b => true, nu => true, d => true, a => true]) + push!( + test_cases, + Dict(:ode => ode, :funcs => funcs_to_test, :correct => (correct, 1)), + ) + + #-------------------------------------------------------------------------- + + # example with 0 replicas required + ode = @ODEmodel(x'(t) = a * z(t), z'(t) = a * z(t)^2, y(t) = x(t)) + funcs_to_test = [a] + correct = OrderedDict([a => false]) + push!( + test_cases, + Dict(:ode => ode, :funcs => funcs_to_test, :correct => (correct, 0)), + ) + + #-------------------------------------------------------------------------- + + for case in test_cases + result = assess_local_identifiability( + case[:ode], + funcs_to_check = case[:funcs], + prob_threshold = 0.932, + type = :ME, ) - - #-------------------------------------------------------------------------- - - for case in test_cases - result = assess_local_identifiability( - case[:ode], - funcs_to_check = case[:funcs], - prob_threshold = 0.932, - type = :ME, - ) - @test result == case[:correct] - end + @test result == case[:correct] end end diff --git a/test/logging.jl b/test/logging.jl index 054a4596..b4c5e9fb 100644 --- a/test/logging.jl +++ b/test/logging.jl @@ -1,21 +1,19 @@ -if GROUP == "All" || GROUP == "Core" - # An attempt to test logging - using Logging +# An attempt to test logging +using Logging - @testset "Logging" begin - ode = StructuralIdentifiability.@ODEmodel(x'(t) = x^42, y(t) = x) +@testset "Logging" begin + ode = StructuralIdentifiability.@ODEmodel(x'(t) = x^42, y(t) = x) - # Some logs - @test_logs StructuralIdentifiability.assess_identifiability(ode) - # No logs - @test_logs min_level = Logging.Warn StructuralIdentifiability.assess_identifiability( - ode, - loglevel = Logging.Warn, - ) - # Many logs - @test_logs StructuralIdentifiability.assess_identifiability( - ode, - loglevel = Logging.Debug, - ) - end + # Some logs + @test_logs StructuralIdentifiability.assess_identifiability(ode) + # No logs + @test_logs min_level = Logging.Warn StructuralIdentifiability.assess_identifiability( + ode, + loglevel = Logging.Warn, + ) + # Many logs + @test_logs StructuralIdentifiability.assess_identifiability( + ode, + loglevel = Logging.Debug, + ) end diff --git a/test/monomial_compress.jl b/test/monomial_compress.jl index 2eded4cc..697d1525 100644 --- a/test/monomial_compress.jl +++ b/test/monomial_compress.jl @@ -1,20 +1,18 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Monomial compression test" begin - R, (v1, v2, v3, v4) = Nemo.polynomial_ring(Nemo.QQ, ["v1", "v2", "v3", "v4"]) - tests = [ - v1 + v2 - 2, - v4 + v4 * v1 - 3 * v2 * v4, - (v1 + v2 + v2^2) * (v3 + v4), - (v1 + v2^2) * (v3^3 + v4) - 7 * (v1 - 3 - v2) * (v3 - v4^2 - v3^2), - ] +@testset "Monomial compression test" begin + R, (v1, v2, v3, v4) = Nemo.polynomial_ring(Nemo.QQ, ["v1", "v2", "v3", "v4"]) + tests = [ + v1 + v2 - 2, + v4 + v4 * v1 - 3 * v2 * v4, + (v1 + v2 + v2^2) * (v3 + v4), + (v1 + v2^2) * (v3^3 + v4) - 7 * (v1 - 3 - v2) * (v3 - v4^2 - v3^2), + ] - for t in tests - a, b = StructuralIdentifiability.monomial_compress(t, [v1, v2]) - s = 0 - for (x, y) in zip(a, b) - s += parent_ring_change(x, parent(t)) * parent_ring_change(y, parent(t)) - end - @test s == t + for t in tests + a, b = StructuralIdentifiability.monomial_compress(t, [v1, v2]) + s = 0 + for (x, y) in zip(a, b) + s += parent_ring_change(x, parent(t)) * parent_ring_change(y, parent(t)) end + @test s == t end end diff --git a/test/ode.jl b/test/ode.jl index 96e40ec5..f8035a7e 100644 --- a/test/ode.jl +++ b/test/ode.jl @@ -1,20 +1,18 @@ -if GROUP == "All" || GROUP == "Core" - @testset "ODE struct" begin - # adding outputs - ode = StructuralIdentifiability.@ODEmodel(x'(t) = x(t) + a) - ode = StructuralIdentifiability.add_outputs(ode, Dict("y" => a * x)) - @test StructuralIdentifiability.AbstractAlgebra.nvars(parent(ode)) == 3 +@testset "ODE struct" begin + # adding outputs + ode = StructuralIdentifiability.@ODEmodel(x'(t) = x(t) + a) + ode = StructuralIdentifiability.add_outputs(ode, Dict("y" => a * x)) + @test StructuralIdentifiability.AbstractAlgebra.nvars(parent(ode)) == 3 - # two or more equations on the same variable - @test_throws DomainError StructuralIdentifiability.@ODEmodel( - x1'(t) = x1(t) + a, - x1'(t) = x1(t) + b, - y(t) = x1 - ) - @test_throws DomainError StructuralIdentifiability.@ODEmodel( - x1'(t) = x1(t), - y(t) = x1, - y(t) = x1 - ) - end + # two or more equations on the same variable + @test_throws DomainError StructuralIdentifiability.@ODEmodel( + x1'(t) = x1(t) + a, + x1'(t) = x1(t) + b, + y(t) = x1 + ) + @test_throws DomainError StructuralIdentifiability.@ODEmodel( + x1'(t) = x1(t), + y(t) = x1, + y(t) = x1 + ) end diff --git a/test/ode_ps_solution.jl b/test/ode_ps_solution.jl index 1a072a7f..ff256b27 100644 --- a/test/ode_ps_solution.jl +++ b/test/ode_ps_solution.jl @@ -1,104 +1,102 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Power series solution for an ODE system" begin - R, (x, x_dot) = Nemo.polynomial_ring(Nemo.QQ, ["x", "x_dot"]) - exp_t = ps_ode_solution( - [x_dot - x], - Dict{QQMPolyRingElem, QQFieldElem}(x => 1), - Dict{QQMPolyRingElem, Array{QQFieldElem, 1}}(), - 20, - )[x] - @test valuation(ps_diff(exp_t) - exp_t) == 19 +@testset "Power series solution for an ODE system" begin + R, (x, x_dot) = Nemo.polynomial_ring(Nemo.QQ, ["x", "x_dot"]) + exp_t = ps_ode_solution( + [x_dot - x], + Dict{QQMPolyRingElem, QQFieldElem}(x => 1), + Dict{QQMPolyRingElem, Array{QQFieldElem, 1}}(), + 20, + )[x] + @test valuation(ps_diff(exp_t) - exp_t) == 19 - R, (x, y, x_dot, y_dot, u) = - Nemo.polynomial_ring(Nemo.QQ, ["x", "y", "x_dot", "y_dot", "u"]) - prec = 100 - eqs = [x_dot - x + 3 * x * y - u, y_dot + 2 * y - 4 * x * y] - u_coeff = [rand(1:5) for i in 1:prec] - sol = ps_ode_solution(eqs, Dict(x => 0, y => -2), Dict(u => u_coeff), prec) - @test map(e -> valuation(evaluate(e, [sol[v] for v in gens(R)])), eqs) == [prec - 1, prec - 1] + R, (x, y, x_dot, y_dot, u) = + Nemo.polynomial_ring(Nemo.QQ, ["x", "y", "x_dot", "y_dot", "u"]) + prec = 100 + eqs = [x_dot - x + 3 * x * y - u, y_dot + 2 * y - 4 * x * y] + u_coeff = [rand(1:5) for i in 1:prec] + sol = ps_ode_solution(eqs, Dict(x => 0, y => -2), Dict(u => u_coeff), prec) + @test map(e -> valuation(evaluate(e, [sol[v] for v in gens(R)])), eqs) == [prec - 1, prec - 1] - F = Nemo.Native.GF(2^31 - 1) - prec = 100 + F = Nemo.Native.GF(2^31 - 1) + prec = 100 - # Testing the function ps_ode_solution by itself - for i in 1:30 - # Setting up the ring - NUMX = 5 - NUMU = 3 - varnames = vcat( - ["x_$(i)_dot" for i in 1:NUMX], - ["x_$i" for i in 1:NUMX], - ["u_$i" for i in 1:NUMU], - ) - R, vars = Nemo.polynomial_ring(F, varnames) + # Testing the function ps_ode_solution by itself + for i in 1:30 + # Setting up the ring + NUMX = 5 + NUMU = 3 + varnames = vcat( + ["x_$(i)_dot" for i in 1:NUMX], + ["x_$i" for i in 1:NUMX], + ["u_$i" for i in 1:NUMU], + ) + R, vars = Nemo.polynomial_ring(F, varnames) - # Generating the initial conditions and inputs - ic = Dict(vars[i + NUMX] => F(rand(-5:5)) for i in 1:NUMX) - inputs = - Dict(u => [F(rand(-3:3)) for i in 1:prec] for u in vars[(2 * NUMX + 1):end]) - # a dictionary for evaluation at zero (to avoid singularities) - eval_at_zero = merge(ic, Dict(u => val[1] for (u, val) in inputs)) + # Generating the initial conditions and inputs + ic = Dict(vars[i + NUMX] => F(rand(-5:5)) for i in 1:NUMX) + inputs = + Dict(u => [F(rand(-3:3)) for i in 1:prec] for u in vars[(2 * NUMX + 1):end]) + # a dictionary for evaluation at zero (to avoid singularities) + eval_at_zero = merge(ic, Dict(u => val[1] for (u, val) in inputs)) - # Generating denominators not vanishing at t = 0 + # Generating denominators not vanishing at t = 0 + denominators = [rand_poly(1, vars[(NUMX + 1):end]) for i in 1:NUMX] + while any([eval_at_dict(p, eval_at_zero) == 0 for p in denominators]) denominators = [rand_poly(1, vars[(NUMX + 1):end]) for i in 1:NUMX] - while any([eval_at_dict(p, eval_at_zero) == 0 for p in denominators]) - denominators = [rand_poly(1, vars[(NUMX + 1):end]) for i in 1:NUMX] - end + end - eqs = [ - denominators[i] * vars[i] - rand_poly(2, vars[(NUMX + 1):end]) for - i in 1:NUMX - ] - @time sol = ps_ode_solution(eqs, ic, inputs, prec) - evals = map(e -> valuation(evaluate(e, [sol[v] for v in gens(R)])), eqs) - for e in evals - @test e >= prec - 1 - end + eqs = [ + denominators[i] * vars[i] - rand_poly(2, vars[(NUMX + 1):end]) for + i in 1:NUMX + ] + @time sol = ps_ode_solution(eqs, ic, inputs, prec) + evals = map(e -> valuation(evaluate(e, [sol[v] for v in gens(R)])), eqs) + for e in evals + @test e >= prec - 1 end + end - # Testing ps_ode_solution in conjuntion with the ODE class - for i in 1:30 - # Setting up the ring - NUMX = 3 - NUMP = 3 - NUMU = 2 - varnames = vcat( - ["x_$i" for i in 1:NUMX], - ["p_$i" for i in 1:NUMP], - ["u_$i" for i in 1:NUMU], - ) - R, vars = Nemo.polynomial_ring(F, varnames) - PType = fpMPolyRingElem - TDict = Dict{PType, Union{PType, Generic.Frac{PType}}} + # Testing ps_ode_solution in conjuntion with the ODE class + for i in 1:30 + # Setting up the ring + NUMX = 3 + NUMP = 3 + NUMU = 2 + varnames = vcat( + ["x_$i" for i in 1:NUMX], + ["p_$i" for i in 1:NUMP], + ["u_$i" for i in 1:NUMU], + ) + R, vars = Nemo.polynomial_ring(F, varnames) + PType = fpMPolyRingElem + TDict = Dict{PType, Union{PType, Generic.Frac{PType}}} - # Generating the intial conditions etc - ic = Dict(vars[i] => F(rand(-5:5)) for i in 1:NUMX) - param_vals = Dict(vars[i + NUMX] => F(rand(-5:5)) for i in 1:NUMP) - inputs = Dict( - u => [F(rand(-3:3)) for i in 1:prec] for u in vars[(NUMX + NUMP + 1):end] - ) - eval_at_zero = merge(ic, param_vals, Dict(u => val[1] for (u, val) in inputs)) + # Generating the intial conditions etc + ic = Dict(vars[i] => F(rand(-5:5)) for i in 1:NUMX) + param_vals = Dict(vars[i + NUMX] => F(rand(-5:5)) for i in 1:NUMP) + inputs = Dict( + u => [F(rand(-3:3)) for i in 1:prec] for u in vars[(NUMX + NUMP + 1):end] + ) + eval_at_zero = merge(ic, param_vals, Dict(u => val[1] for (u, val) in inputs)) - # Generating denominators not vanishing at t = 0 + # Generating denominators not vanishing at t = 0 + denominators = [rand_poly(1, vars) for i in 1:NUMX] + while any([eval_at_dict(p, eval_at_zero) == 0 for p in denominators]) denominators = [rand_poly(1, vars) for i in 1:NUMX] - while any([eval_at_dict(p, eval_at_zero) == 0 for p in denominators]) - denominators = [rand_poly(1, vars) for i in 1:NUMX] - end + end - eqs = TDict(vars[i] => rand_poly(2, vars) // denominators[i] for i in 1:NUMX) - ode = ODE{PType}(eqs, TDict(), vars[(NUMX + NUMP + 1):end]) - @time sol = power_series_solution(ode, param_vals, ic, inputs, prec) - ps_ring = parent(collect(values(sol))[1]) - for (p, val) in param_vals - sol[p] = ps_ring(val) - end - eval_point = [sol[v] for v in vars] - for (xd, f) in eqs - num, den = unpack_fraction(f) - lhs = divexact(evaluate(num, eval_point), evaluate(den, eval_point)) - rhs = ps_diff(sol[xd]) - @test valuation(lhs - rhs) >= prec - 1 - end + eqs = TDict(vars[i] => rand_poly(2, vars) // denominators[i] for i in 1:NUMX) + ode = ODE{PType}(eqs, TDict(), vars[(NUMX + NUMP + 1):end]) + @time sol = power_series_solution(ode, param_vals, ic, inputs, prec) + ps_ring = parent(collect(values(sol))[1]) + for (p, val) in param_vals + sol[p] = ps_ring(val) + end + eval_point = [sol[v] for v in vars] + for (xd, f) in eqs + num, den = unpack_fraction(f) + lhs = divexact(evaluate(num, eval_point), evaluate(den, eval_point)) + rhs = ps_diff(sol[xd]) + @test valuation(lhs - rhs) >= prec - 1 end end end diff --git a/test/paradigm_shift.jl b/test/paradigm_shift.jl index 6a76f2e0..553e9231 100644 --- a/test/paradigm_shift.jl +++ b/test/paradigm_shift.jl @@ -1,271 +1,269 @@ -if GROUP == "All" || GROUP == "Core" - ≡(A, B) = A ⊆ B && B ⊆ A +≡(A, B) = A ⊆ B && B ⊆ A - function test_reparametrization(old_ode, new_ode, var_mapping, implicit_relations) - # NOTE: should this be strengthened to == ? - # @test map(string, old_ode.u_vars) ≡ map(string, new_ode.u_vars) - # @test map(string, old_ode.y_vars) ≡ map(string, new_ode.y_vars) - @test length(old_ode.y_vars) == length(new_ode.y_vars) - @test length(old_ode.u_vars) == length(new_ode.u_vars) - @test base_ring(parent(first(values(var_mapping)))) == parent(old_ode) +function test_reparametrization(old_ode, new_ode, var_mapping, implicit_relations) + # NOTE: should this be strengthened to == ? + # @test map(string, old_ode.u_vars) ≡ map(string, new_ode.u_vars) + # @test map(string, old_ode.y_vars) ≡ map(string, new_ode.y_vars) + @test length(old_ode.y_vars) == length(new_ode.y_vars) + @test length(old_ode.u_vars) == length(new_ode.u_vars) + @test base_ring(parent(first(values(var_mapping)))) == parent(old_ode) - # We check that the PS solutions of the new_ode coincide with the solutions - # of the old_ode projected onto the new variables - ord = 5 - # NOTE: there may be an unlucky specialization point, and newton iteration - # will fail, and tests will fail as a result - bound = 100 - old_params = old_ode.parameters - old_vars = old_ode.x_vars - old_inputs = old_ode.u_vars - param_spec_point = map(Nemo.QQ, rand(1:bound, length(old_params))) - old_param_spec = Dict(old_params .=> param_spec_point) - ic_point = map(Nemo.QQ, rand(1:bound, length(old_vars))) - old_var_ic = Dict(old_vars .=> ic_point) - input_ts = map(_ -> [Nemo.QQ(rand(1:bound))], 1:length(old_inputs)) - old_input_ts = - Dict{eltype(old_vars), Vector{Nemo.QQFieldElem}}(old_inputs .=> input_ts) + # We check that the PS solutions of the new_ode coincide with the solutions + # of the old_ode projected onto the new variables + ord = 5 + # NOTE: there may be an unlucky specialization point, and newton iteration + # will fail, and tests will fail as a result + bound = 100 + old_params = old_ode.parameters + old_vars = old_ode.x_vars + old_inputs = old_ode.u_vars + param_spec_point = map(Nemo.QQ, rand(1:bound, length(old_params))) + old_param_spec = Dict(old_params .=> param_spec_point) + ic_point = map(Nemo.QQ, rand(1:bound, length(old_vars))) + old_var_ic = Dict(old_vars .=> ic_point) + input_ts = map(_ -> [Nemo.QQ(rand(1:bound))], 1:length(old_inputs)) + old_input_ts = + Dict{eltype(old_vars), Vector{Nemo.QQFieldElem}}(old_inputs .=> input_ts) - old_solutions = StructuralIdentifiability.power_series_solution( - old_ode, - old_param_spec, - old_var_ic, - old_input_ts, - ord, - ) - - new_params = new_ode.parameters - new_vars = new_ode.x_vars - new_inputs = new_ode.u_vars - new_param_spec = Dict( - new_param => StructuralIdentifiability.eval_at_dict( - var_mapping[new_param], - old_param_spec, - ) for new_param in new_params - ) - new_var_ic = Dict( - new_var => StructuralIdentifiability.eval_at_dict( - var_mapping[new_var], - merge(old_param_spec, old_var_ic), - ) for new_var in new_vars - ) - new_input_ts = Dict{eltype(new_vars), Vector{Nemo.QQFieldElem}}( - new_input => old_input_ts[numerator(var_mapping[new_input])] for - new_input in new_inputs - ) + old_solutions = StructuralIdentifiability.power_series_solution( + old_ode, + old_param_spec, + old_var_ic, + old_input_ts, + ord, + ) - new_solutions = StructuralIdentifiability.power_series_solution( - new_ode, - new_param_spec, - new_var_ic, - new_input_ts, - ord, - ) + new_params = new_ode.parameters + new_vars = new_ode.x_vars + new_inputs = new_ode.u_vars + new_param_spec = Dict( + new_param => StructuralIdentifiability.eval_at_dict( + var_mapping[new_param], + old_param_spec, + ) for new_param in new_params + ) + new_var_ic = Dict( + new_var => StructuralIdentifiability.eval_at_dict( + var_mapping[new_var], + merge(old_param_spec, old_var_ic), + ) for new_var in new_vars + ) + new_input_ts = Dict{eltype(new_vars), Vector{Nemo.QQFieldElem}}( + new_input => old_input_ts[numerator(var_mapping[new_input])] for + new_input in new_inputs + ) - # NOTE: test that y's are mapped one to one! - @test map(string, [var_mapping[output] for output in new_ode.y_vars]) ≡ map(string, old_ode.y_vars) + new_solutions = StructuralIdentifiability.power_series_solution( + new_ode, + new_param_spec, + new_var_ic, + new_input_ts, + ord, + ) - # Test IO behavior - for var in vcat(new_ode.y_vars) - new_solution = new_solutions[var] - old_solution = old_solutions[numerator(var_mapping[var])] - @test new_solution == old_solution - end + # NOTE: test that y's are mapped one to one! + @test map(string, [var_mapping[output] for output in new_ode.y_vars]) ≡ map(string, old_ode.y_vars) - # Test state dynamics - projected_solutions = Dict( - var => - StructuralIdentifiability.eval_at_dict(var_mapping[var], old_solutions) - for var in new_ode.x_vars - ) - for var in vcat(new_ode.x_vars) - new_solution = new_solutions[var] - prj_solution = projected_solutions[var] - @test new_solution == prj_solution - end + # Test IO behavior + for var in vcat(new_ode.y_vars) + new_solution = new_solutions[var] + old_solution = old_solutions[numerator(var_mapping[var])] + @test new_solution == old_solution + end - # Test relations - for relation in implicit_relations - @test iszero(StructuralIdentifiability.eval_at_dict(relation, var_mapping)) - end + # Test state dynamics + projected_solutions = Dict( + var => + StructuralIdentifiability.eval_at_dict(var_mapping[var], old_solutions) + for var in new_ode.x_vars + ) + for var in vcat(new_ode.x_vars) + new_solution = new_solutions[var] + prj_solution = projected_solutions[var] + @test new_solution == prj_solution + end - return nothing + # Test relations + for relation in implicit_relations + @test iszero(StructuralIdentifiability.eval_at_dict(relation, var_mapping)) end - cases = [ - ( - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = x1(t) + a + u1(t), - x2'(t) = x2(t) + b + u2(t), - y1(t) = x1(t), - y2(t) = x2(t), - ), + return nothing +end + +cases = [ + ( + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = x1(t) + a + u1(t), + x2'(t) = x2(t) + b + u2(t), + y1(t) = x1(t), + y2(t) = x2(t), ), - ( - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = a * x1, - x2'(t) = b * x2, - y(t) = x1 + x2 - ), + ), + ( + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = a * x1, + x2'(t) = b * x2, + y(t) = x1 + x2 ), - ( - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = (a + b) * x1 // x2 + a, - x2'(t) = x1 // (a + c) + c, - y(t) = x1 - ), + ), + ( + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = (a + b) * x1 // x2 + a, + x2'(t) = x1 // (a + c) + c, + y(t) = x1 ), - ( - ode = StructuralIdentifiability.@ODEmodel( - T1'(t) = a * T1 - b * T1 * T2 + u(t), - T2'(t) = -c * T2 + d * T1 * T2, - y(t) = T1 - ), + ), + ( + ode = StructuralIdentifiability.@ODEmodel( + T1'(t) = a * T1 - b * T1 * T2 + u(t), + T2'(t) = -c * T2 + d * T1 * T2, + y(t) = T1 ), - ( - #= - Identifiable functions: - x1, Θ * x2 + ), + ( + #= + Identifiable functions: + x1, Θ * x2 - Under - T1 = x1, - T2 = Θ * x2 + Under + T1 = x1, + T2 = Θ * x2 - Reparametrizes into: - T1' = T1 + T2, - T2' = 0, - y = T1 - =# - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = x1(t) + Θ * x2(t), - x2'(t) = 0, - y(t) = x1(t) - ), + Reparametrizes into: + T1' = T1 + T2, + T2' = 0, + y = T1 + =# + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = x1(t) + Θ * x2(t), + x2'(t) = 0, + y(t) = x1(t) ), - ( - #= - Identifiable functions: - x1, α * x2, α * x3, α * C + ), + ( + #= + Identifiable functions: + x1, α * x2, α * x3, α * C - Under - T1 = α * x2, - T2 = α * x3, - T3 = x1, - T4 = α * C + Under + T1 = α * x2, + T2 = α * x3, + T3 = x1, + T4 = α * C - Reparametrizes into: - T3'(t) = T1(t) - T2'(t) = T4 - T1'(t) = T2(t) - y(t) = T3(t) - =# - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = α * x2(t), - x2'(t) = x3(t), - x3'(t) = C, - y(t) = x1(t) - ), + Reparametrizes into: + T3'(t) = T1(t) + T2'(t) = T4 + T1'(t) = T2(t) + y(t) = T3(t) + =# + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = α * x2(t), + x2'(t) = x3(t), + x3'(t) = C, + y(t) = x1(t) ), - ( - #= - Identifiable functions: - x1^2 + x2^2, α + ), + ( + #= + Identifiable functions: + x1^2 + x2^2, α - Under - T1 = x1^2 + x2^2, - T2 = α + Under + T1 = x1^2 + x2^2, + T2 = α - Reparametrizes into: - T1' = 2*T1*T2 - y = 1//2*T1 - =# - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = α * (x1 - x2), - x2'(t) = α * (x1 + x2), - y(t) = (x1^2 + x2^2) // 2, - ), + Reparametrizes into: + T1' = 2*T1*T2 + y = 1//2*T1 + =# + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = α * (x1 - x2), + x2'(t) = α * (x1 + x2), + y(t) = (x1^2 + x2^2) // 2, ), - ( - # Pivastatin - # Reparametrizes into a 4-dimensional nonlinear model with no algebraic - # relations -- how is this even legal?? - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = - k3 * x3(t) - r3 * x1(t) - k1 * x1(t) * (T0 - x2(t)) + r1 * x2(t), - x2'(t) = k1 * x1(t) * (T0 - x2(t)) - (r1 + k2) * x2(t), - x3'(t) = r3 * x1(t) - (k3 + k4) * x3(t) + k2 * x2(t), - y1(t) = k * (x2(t) + x3(t)) - ), + ), + ( + # Pivastatin + # Reparametrizes into a 4-dimensional nonlinear model with no algebraic + # relations -- how is this even legal?? + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = + k3 * x3(t) - r3 * x1(t) - k1 * x1(t) * (T0 - x2(t)) + r1 * x2(t), + x2'(t) = k1 * x1(t) * (T0 - x2(t)) - (r1 + k2) * x2(t), + x3'(t) = r3 * x1(t) - (k3 + k4) * x3(t) + k2 * x2(t), + y1(t) = k * (x2(t) + x3(t)) ), - ( - # Transfection_4State - ode = StructuralIdentifiability.@ODEmodel( - mRNA'(t) = -d1 * mRNA(t) - d2 * mRNA(t) * enz(t), - GFP'(t) = kTL * mRNA(t) - b * GFP(t), - enz'(t) = d3 * mRNAenz(t) - d2 * mRNA(t) * enz(t), - mRNAenz'(t) = -d3 * mRNAenz(t) + d2 * mRNA(t) * enz(t), - y1(t) = GFP(t) - ), + ), + ( + # Transfection_4State + ode = StructuralIdentifiability.@ODEmodel( + mRNA'(t) = -d1 * mRNA(t) - d2 * mRNA(t) * enz(t), + GFP'(t) = kTL * mRNA(t) - b * GFP(t), + enz'(t) = d3 * mRNAenz(t) - d2 * mRNA(t) * enz(t), + mRNAenz'(t) = -d3 * mRNAenz(t) + d2 * mRNA(t) * enz(t), + y1(t) = GFP(t) ), - ( - # LLW1987_io - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = -p1 * x1(t) + p2 * u(t), - x2'(t) = -p3 * x2(t) + p4 * u(t), - x3'(t) = -(p1 + p3) * x3(t) + (p4 * x1(t) + p2 * x2(t)) * u(t), - y1(t) = x3(t) - ), + ), + ( + # LLW1987_io + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = -p1 * x1(t) + p2 * u(t), + x2'(t) = -p3 * x2(t) + p4 * u(t), + x3'(t) = -(p1 + p3) * x3(t) + (p4 * x1(t) + p2 * x2(t)) * u(t), + y1(t) = x3(t) ), - ( - # Take care of monomial orders in GB! - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = x1 + x2^2 + a^2, - x2'(t) = x2 + a * d^3, - y(t) = x1 - ), + ), + ( + # Take care of monomial orders in GB! + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = x1 + x2^2 + a^2, + x2'(t) = x2 + a * d^3, + y(t) = x1 ), - ( - # SLIQR system - ode = StructuralIdentifiability.@ODEmodel( - S'(t) = -b * In(t) * S(t) * Ninv - u(t) * S(t) * Ninv, - L'(t) = b * In(t) * S(t) * Ninv - a * L(t), - In'(t) = a * L(t) - g * In(t) + s * Q(t), - Q'(t) = (1 - e) * g * In(t) - s * Q(t), - y(t) = In(t) * Ninv - ), + ), + ( + # SLIQR system + ode = StructuralIdentifiability.@ODEmodel( + S'(t) = -b * In(t) * S(t) * Ninv - u(t) * S(t) * Ninv, + L'(t) = b * In(t) * S(t) * Ninv - a * L(t), + In'(t) = a * L(t) - g * In(t) + s * Q(t), + Q'(t) = (1 - e) * g * In(t) - s * Q(t), + y(t) = In(t) * Ninv ), - ( - ode = StructuralIdentifiability.@ODEmodel( - x1'(t) = a * x1 + b * x2 + u(t), - x2'(t) = b * x1 + c * x2, - y(t) = x1 - ), + ), + ( + ode = StructuralIdentifiability.@ODEmodel( + x1'(t) = a * x1 + b * x2 + u(t), + x2'(t) = b * x1 + c * x2, + y(t) = x1 ), - ] + ), +] - @testset "Global reparametrizations" begin - # Test that variables are mapped properly - ode = cases[1].ode - (new_ode, new_vars, implicit_relations) = - StructuralIdentifiability.reparametrize_global(ode) - @test length(new_vars) == length(gens(parent(new_ode))) == 8 - @test length(new_ode.x_vars) == 2 - # @test map(string, new_ode.y_vars) ≡ map(string, ode.y_vars) - # @test map(string, new_ode.u_vars) ≡ map(string, ode.u_vars) - @test length(new_ode.parameters) == 2 - @test isempty(implicit_relations) +@testset "Global reparametrizations" begin + # Test that variables are mapped properly + ode = cases[1].ode + (new_ode, new_vars, implicit_relations) = + StructuralIdentifiability.reparametrize_global(ode) + @test length(new_vars) == length(gens(parent(new_ode))) == 8 + @test length(new_ode.x_vars) == 2 + # @test map(string, new_ode.y_vars) ≡ map(string, ode.y_vars) + # @test map(string, new_ode.u_vars) ≡ map(string, ode.u_vars) + @test length(new_ode.parameters) == 2 + @test isempty(implicit_relations) - # Test that relations are functional - ode = cases[2].ode + # Test that relations are functional + ode = cases[2].ode + (new_ode, new_vars, implicit_relations) = + StructuralIdentifiability.reparametrize_global(ode) + @test length(implicit_relations) == 1 + + for case in cases + ode = case.ode (new_ode, new_vars, implicit_relations) = StructuralIdentifiability.reparametrize_global(ode) - @test length(implicit_relations) == 1 - - for case in cases - ode = case.ode - (new_ode, new_vars, implicit_relations) = - StructuralIdentifiability.reparametrize_global(ode) - test_reparametrization(ode, new_ode, new_vars, implicit_relations) - end + test_reparametrization(ode, new_ode, new_vars, implicit_relations) end end diff --git a/test/parent_ring_change.jl b/test/parent_ring_change.jl index 9e940301..6b44201c 100644 --- a/test/parent_ring_change.jl +++ b/test/parent_ring_change.jl @@ -1,55 +1,53 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Parent ring change" begin - for field in [Nemo.QQ, Nemo.Native.GF(2^31 - 1)] - R, (z, x, y) = polynomial_ring(QQ, ["z", "x", "y"], ordering = :degrevlex) - R_, (y_, x_) = polynomial_ring(QQ, ["y", "x"], ordering = :lex) - R__, (x__, t__, y__, z__) = - polynomial_ring(QQ, ["x", "t", "y", "z"], ordering = :deglex) +@testset "Parent ring change" begin + for field in [Nemo.QQ, Nemo.Native.GF(2^31 - 1)] + R, (z, x, y) = polynomial_ring(QQ, ["z", "x", "y"], ordering = :degrevlex) + R_, (y_, x_) = polynomial_ring(QQ, ["y", "x"], ordering = :lex) + R__, (x__, t__, y__, z__) = + polynomial_ring(QQ, ["x", "t", "y", "z"], ordering = :deglex) - f = 2x + 3y + x^7 * y - @test f == - StructuralIdentifiability.parent_ring_change(f, R, matching = :byname) - @test f == - StructuralIdentifiability.parent_ring_change(f, R, matching = :byindex) + f = 2x + 3y + x^7 * y + @test f == + StructuralIdentifiability.parent_ring_change(f, R, matching = :byname) + @test f == + StructuralIdentifiability.parent_ring_change(f, R, matching = :byindex) - f_ = StructuralIdentifiability.parent_ring_change(f, R_, matching = :byname) - f__ = StructuralIdentifiability.parent_ring_change(f, R__, matching = :byname) - @test f_ == 2x_ + 3y_ + x_^7 * y_ - @test f__ == 2x__ + 3y__ + x__^7 * y__ - @test f == - StructuralIdentifiability.parent_ring_change(f_, R, matching = :byname) - @test f == - StructuralIdentifiability.parent_ring_change(f__, R, matching = :byname) + f_ = StructuralIdentifiability.parent_ring_change(f, R_, matching = :byname) + f__ = StructuralIdentifiability.parent_ring_change(f, R__, matching = :byname) + @test f_ == 2x_ + 3y_ + x_^7 * y_ + @test f__ == 2x__ + 3y__ + x__^7 * y__ + @test f == + StructuralIdentifiability.parent_ring_change(f_, R, matching = :byname) + @test f == + StructuralIdentifiability.parent_ring_change(f__, R, matching = :byname) - @test_throws ArgumentError StructuralIdentifiability.parent_ring_change( - x + z, - R_, - matching = :byname, - ) + @test_throws ArgumentError StructuralIdentifiability.parent_ring_change( + x + z, + R_, + matching = :byname, + ) - f = 3y - f_ = StructuralIdentifiability.parent_ring_change(f, R__, matching = :byname) - @test f_ == 3y__ + f = 3y + f_ = StructuralIdentifiability.parent_ring_change(f, R__, matching = :byname) + @test f_ == 3y__ - f__ = 2x__ + 5t__^3 + 3y__^2 - f = StructuralIdentifiability.parent_ring_change(f__, R, matching = :byindex) - @test_throws ArgumentError StructuralIdentifiability.parent_ring_change( - f__, - R_, - matching = :byindex, - ) - @test f == 2z + 5x^3 + 3y^2 - @test f__ == - StructuralIdentifiability.parent_ring_change(f, R__, matching = :byindex) + f__ = 2x__ + 5t__^3 + 3y__^2 + f = StructuralIdentifiability.parent_ring_change(f__, R, matching = :byindex) + @test_throws ArgumentError StructuralIdentifiability.parent_ring_change( + f__, + R_, + matching = :byindex, + ) + @test f == 2z + 5x^3 + 3y^2 + @test f__ == + StructuralIdentifiability.parent_ring_change(f, R__, matching = :byindex) - R, (x,) = polynomial_ring(field, ["x"]) - R2, (x2,) = polynomial_ring(Nemo.fraction_field(R), ["x"]) - R3, (x3,) = polynomial_ring(field, ["x"]) - f = x3^2 - f_ = StructuralIdentifiability.parent_ring_change(f, R2) - @test parent(f) == R3 - @test parent(f_) == R2 - @test repr(f_) == "x^2" - end + R, (x,) = polynomial_ring(field, ["x"]) + R2, (x2,) = polynomial_ring(Nemo.fraction_field(R), ["x"]) + R3, (x3,) = polynomial_ring(field, ["x"]) + f = x3^2 + f_ = StructuralIdentifiability.parent_ring_change(f, R2) + @test parent(f) == R3 + @test parent(f_) == R2 + @test repr(f_) == "x^2" end end diff --git a/test/pb_representation.jl b/test/pb_representation.jl index ad89fcf8..245c0a71 100644 --- a/test/pb_representation.jl +++ b/test/pb_representation.jl @@ -1,24 +1,22 @@ -if GROUP == "All" || GROUP == "Core" - @testset "PB-representation - creation" begin - ode = @ODEmodel(x1'(t) = x2(t), x2'(t) = a * x1(t), y(t) = x1(t)) - ioeqs = find_ioequations(ode) - pbr = PBRepresentation(ode, ioeqs) - @test pbr.profile == Dict("y(t)" => 2) - @test pbr.u_names == Array{String, 1}() - @test pbr.y_names == ["y(t)"] - @test pbr.param_names == ["a"] +@testset "PB-representation - creation" begin + ode = @ODEmodel(x1'(t) = x2(t), x2'(t) = a * x1(t), y(t) = x1(t)) + ioeqs = find_ioequations(ode) + pbr = PBRepresentation(ode, ioeqs) + @test pbr.profile == Dict("y(t)" => 2) + @test pbr.u_names == Array{String, 1}() + @test pbr.y_names == ["y(t)"] + @test pbr.param_names == ["a"] - ode = @ODEmodel( - x1'(t) = x1(t), - x2'(t) = a * x2(t), - y(t) = x1(t), - y2(t) = x2(t) + u(t) - ) - ioeqs = find_ioequations(ode) - pbr = PBRepresentation(ode, ioeqs) - @test pbr.profile == Dict("y(t)" => 1, "y2(t)" => 1) - @test pbr.u_names == ["u(t)"] - @test pbr.y_names == ["y(t)", "y2(t)"] - @test pbr.param_names == ["a"] - end + ode = @ODEmodel( + x1'(t) = x1(t), + x2'(t) = a * x2(t), + y(t) = x1(t), + y2(t) = x2(t) + u(t) + ) + ioeqs = find_ioequations(ode) + pbr = PBRepresentation(ode, ioeqs) + @test pbr.profile == Dict("y(t)" => 1, "y2(t)" => 1) + @test pbr.u_names == ["u(t)"] + @test pbr.y_names == ["y(t)", "y2(t)"] + @test pbr.param_names == ["a"] end diff --git a/test/ps_diff.jl b/test/ps_diff.jl index 9368ac85..1e40ed23 100644 --- a/test/ps_diff.jl +++ b/test/ps_diff.jl @@ -1,16 +1,14 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Power Series Differentiation" begin - T, t = Nemo.power_series_ring(Nemo.QQ, 300, "t"; model = :capped_absolute) +@testset "Power Series Differentiation" begin + T, t = Nemo.power_series_ring(Nemo.QQ, 300, "t"; model = :capped_absolute) - @test ps_diff(zero(T)) == zero(T) + @test ps_diff(zero(T)) == zero(T) - @test ps_diff(one(T)) == zero(T) + @test ps_diff(one(T)) == zero(T) - @test ps_diff(gen(T)) == one(T) + @test ps_diff(gen(T)) == one(T) - @test ps_diff(t^3 - t^4 * (1 // 8) + 5 * t^10) == - 3 * t^2 - t^3 * (1 // 2) + 50 * t^9 + @test ps_diff(t^3 - t^4 * (1 // 8) + 5 * t^10) == + 3 * t^2 - t^3 * (1 // 2) + 50 * t^9 - @test ps_diff((1 + t)^1000) == truncate(1000 * (1 + t)^999, 299) - end + @test ps_diff((1 + t)^1000) == truncate(1000 * (1 + t)^999, 299) end diff --git a/test/ps_integrate.jl b/test/ps_integrate.jl index 79ae5b69..0bfc5c45 100644 --- a/test/ps_integrate.jl +++ b/test/ps_integrate.jl @@ -1,17 +1,15 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Power series integration" begin - T, t = Nemo.power_series_ring(Nemo.QQ, 300, "t"; model = :capped_absolute) +@testset "Power series integration" begin + T, t = Nemo.power_series_ring(Nemo.QQ, 300, "t"; model = :capped_absolute) - @test ps_integrate(zero(T)) == zero(T) + @test ps_integrate(zero(T)) == zero(T) - @test ps_integrate(one(T)) == gen(T) + @test ps_integrate(one(T)) == gen(T) - @test ps_integrate(gen(T)) == gen(T)^2 * (1 // 2) + @test ps_integrate(gen(T)) == gen(T)^2 * (1 // 2) - @test ps_integrate(t^3 - t^4 * (1 // 8) + 5 * t^10) == - t^4 * (1 // 4) - t^5 * (1 // 40) + t^11 * (5 // 11) + @test ps_integrate(t^3 - t^4 * (1 // 8) + 5 * t^10) == + t^4 * (1 // 4) - t^5 * (1 // 40) + t^11 * (5 // 11) - @test ps_integrate((1 + t)^1000) == - truncate((1 + t)^1001 * (1 // 1001) - 1 // 1001, 300) - end + @test ps_integrate((1 + t)^1000) == + truncate((1 + t)^1001 * (1 // 1001) - 1 // 1001, 300) end diff --git a/test/ps_inverse.jl b/test/ps_inverse.jl index 076e891a..361f3858 100644 --- a/test/ps_inverse.jl +++ b/test/ps_inverse.jl @@ -1,28 +1,26 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Power series matrix inverse" begin - T, t = Nemo.power_series_ring( - Nemo.Native.GF(2^31 - 1), - 50, - "t"; - model = :capped_absolute, - ) +@testset "Power series matrix inverse" begin + T, t = Nemo.power_series_ring( + Nemo.Native.GF(2^31 - 1), + 50, + "t"; + model = :capped_absolute, + ) - for d in 1:5 - S = Nemo.matrix_space(T, d, d) - for case in 1:20 + for d in 1:5 + S = Nemo.matrix_space(T, d, d) + for case in 1:20 + M = S([random_ps(T) for i in 1:d, j in 1:d]) + while isequal( + StructuralIdentifiability.LinearAlgebra.det( + StructuralIdentifiability.ps_matrix_const_term(M), + ), + 0, + ) M = S([random_ps(T) for i in 1:d, j in 1:d]) - while isequal( - StructuralIdentifiability.LinearAlgebra.det( - StructuralIdentifiability.ps_matrix_const_term(M), - ), - 0, - ) - M = S([random_ps(T) for i in 1:d, j in 1:d]) - end - invM = ps_matrix_inv(M) - prod = invM * M - @test prod == one(S) end + invM = ps_matrix_inv(M) + prod = invM * M + @test prod == one(S) end end end diff --git a/test/ps_matrix_homlinear.jl b/test/ps_matrix_homlinear.jl index dfb3a916..26b6ac49 100644 --- a/test/ps_matrix_homlinear.jl +++ b/test/ps_matrix_homlinear.jl @@ -1,25 +1,23 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Homogeneous linear differential equations" begin - T, t = Nemo.power_series_ring( - Nemo.Native.GF(2^31 - 1), - 300, - "t"; - model = :capped_absolute, - ) +@testset "Homogeneous linear differential equations" begin + T, t = Nemo.power_series_ring( + Nemo.Native.GF(2^31 - 1), + 300, + "t"; + model = :capped_absolute, + ) - for d in 1:5 - for c in 1:5 - S = Nemo.matrix_space(T, d, d) - A = random_ps_matrix(T, S) - Sconst = Nemo.matrix_space(Nemo.Native.GF(2^31 - 1), d, d) + for d in 1:5 + for c in 1:5 + S = Nemo.matrix_space(T, d, d) + A = random_ps_matrix(T, S) + Sconst = Nemo.matrix_space(Nemo.Native.GF(2^31 - 1), d, d) + Y0 = Sconst([rand(Int) % 100 for i in 1:d, j in 1:d]) + while StructuralIdentifiability.LinearAlgebra.det(Y0) == 0 Y0 = Sconst([rand(Int) % 100 for i in 1:d, j in 1:d]) - while StructuralIdentifiability.LinearAlgebra.det(Y0) == 0 - Y0 = Sconst([rand(Int) % 100 for i in 1:d, j in 1:d]) - end - @time sol, sol_inv = ps_matrix_homlinear_de(A, Y0) - to_be_zero = map(ps_diff, sol) - A * sol - @test truncate_matrix(to_be_zero, 299) == truncate_matrix(zero(S), 299) end + @time sol, sol_inv = ps_matrix_homlinear_de(A, Y0) + to_be_zero = map(ps_diff, sol) - A * sol + @test truncate_matrix(to_be_zero, 299) == truncate_matrix(zero(S), 299) end end end diff --git a/test/ps_matrix_linear.jl b/test/ps_matrix_linear.jl index ead2add4..caddfc97 100644 --- a/test/ps_matrix_linear.jl +++ b/test/ps_matrix_linear.jl @@ -1,23 +1,21 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Linear differential equations" begin - T, t = Nemo.power_series_ring( - Nemo.Native.GF(2^31 - 1), - 300, - "t"; - model = :capped_absolute, - ) +@testset "Linear differential equations" begin + T, t = Nemo.power_series_ring( + Nemo.Native.GF(2^31 - 1), + 300, + "t"; + model = :capped_absolute, + ) - for d in 1:5 - for c in 1:5 - S = Nemo.matrix_space(T, d, d) - A = random_ps_matrix(T, S) - B = random_ps_matrix(T, S) - Sconst = Nemo.matrix_space(Nemo.Native.GF(2^31 - 1), d, d) - Y0 = Sconst([rand(Int) % 100 for i in 1:d, j in 1:d]) - @time sol = ps_matrix_linear_de(A, B, Y0) - to_be_zero = map(ps_diff, sol) - A * sol - B - @test truncate_matrix(to_be_zero, 299) == truncate_matrix(zero(S), 299) - end + for d in 1:5 + for c in 1:5 + S = Nemo.matrix_space(T, d, d) + A = random_ps_matrix(T, S) + B = random_ps_matrix(T, S) + Sconst = Nemo.matrix_space(Nemo.Native.GF(2^31 - 1), d, d) + Y0 = Sconst([rand(Int) % 100 for i in 1:d, j in 1:d]) + @time sol = ps_matrix_linear_de(A, B, Y0) + to_be_zero = map(ps_diff, sol) - A * sol - B + @test truncate_matrix(to_be_zero, 299) == truncate_matrix(zero(S), 299) end end end diff --git a/test/ps_matrix_log.jl b/test/ps_matrix_log.jl index b01a95ad..6a32943a 100644 --- a/test/ps_matrix_log.jl +++ b/test/ps_matrix_log.jl @@ -1,44 +1,42 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Logarith of power series matrices" begin - T, t = Nemo.power_series_ring(Nemo.QQ, 300, "t"; model = :capped_absolute) +@testset "Logarith of power series matrices" begin + T, t = Nemo.power_series_ring(Nemo.QQ, 300, "t"; model = :capped_absolute) - for d in 1:5 - diag_elements = [1 + t * random_ps(T) for i in 1:d] - S = Nemo.matrix_space(T, d, d) - M = S([(i == j ? diag_elements[i] : T(0)) for i in 1:d, j in 1:d]) - result = ps_matrix_log(M) - correct = S([(i == j ? log(diag_elements[i]) : T(0)) for i in 1:d, j in 1:d]) - @test result == correct - end + for d in 1:5 + diag_elements = [1 + t * random_ps(T) for i in 1:d] + S = Nemo.matrix_space(T, d, d) + M = S([(i == j ? diag_elements[i] : T(0)) for i in 1:d, j in 1:d]) + result = ps_matrix_log(M) + correct = S([(i == j ? log(diag_elements[i]) : T(0)) for i in 1:d, j in 1:d]) + @test result == correct + end - for c in 1:5 - f = random_ps(T) * t - S = Nemo.matrix_space(T, 2, 2) - m = S([ - T(1) f - T(0) T(1) - ]) - correct = S([ - T(0) f - T(0) T(0) - ]) - @test ps_matrix_log(m) == correct - end + for c in 1:5 + f = random_ps(T) * t + S = Nemo.matrix_space(T, 2, 2) + m = S([ + T(1) f + T(0) T(1) + ]) + correct = S([ + T(0) f + T(0) T(0) + ]) + @test ps_matrix_log(m) == correct + end - for c in 1:5 - f, g = random_ps(T) * t, random_ps(T) * t - S = Nemo.matrix_space(T, 3, 3) - m = S([ - T(1) f g - T(0) T(1) f - T(0) T(0) T(1) - ]) - correct = S([ - T(0) f (-1 // 2) * f^2+g - T(0) T(0) f - T(0) T(0) T(0) - ]) - @test ps_matrix_log(m) == correct - end + for c in 1:5 + f, g = random_ps(T) * t, random_ps(T) * t + S = Nemo.matrix_space(T, 3, 3) + m = S([ + T(1) f g + T(0) T(1) f + T(0) T(0) T(1) + ]) + correct = S([ + T(0) f (-1 // 2) * f^2+g + T(0) T(0) f + T(0) T(0) T(0) + ]) + @test ps_matrix_log(m) == correct end end diff --git a/test/pseudodivision.jl b/test/pseudodivision.jl index dec93024..895300ec 100644 --- a/test/pseudodivision.jl +++ b/test/pseudodivision.jl @@ -1,8 +1,6 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Pseudodivision" begin - R, (x, y, z) = Nemo.polynomial_ring(Nemo.QQ, ["x", "y", "z"]) +@testset "Pseudodivision" begin + R, (x, y, z) = Nemo.polynomial_ring(Nemo.QQ, ["x", "y", "z"]) - @test iszero(pseudodivision(x^2 - y^2, x - y, x)) - @test pseudodivision(y * x^4 + x^3 + x, z * x^2, x) == x - end + @test iszero(pseudodivision(x^2 - y^2, x - y, x)) + @test pseudodivision(y * x^4 + x^3 + x, z * x^2, x) == x end diff --git a/test/reduce_ode_mod_p.jl b/test/reduce_ode_mod_p.jl index 55fff7b4..b81b57bb 100644 --- a/test/reduce_ode_mod_p.jl +++ b/test/reduce_ode_mod_p.jl @@ -1,16 +1,14 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Reducing ODE mod p" begin - ode = @ODEmodel( - x'(t) = 1 // 2 * x(t) - 5 * y(t), - y'(t) = (3 // 5 * x(t) + 17 * y(t)) // (y(t) - 1), - z(t) = y(t) - ) +@testset "Reducing ODE mod p" begin + ode = @ODEmodel( + x'(t) = 1 // 2 * x(t) - 5 * y(t), + y'(t) = (3 // 5 * x(t) + 17 * y(t)) // (y(t) - 1), + z(t) = y(t) + ) - ode_red = reduce_ode_mod_p(ode, 17) + ode_red = reduce_ode_mod_p(ode, 17) - x = str_to_var("x(t)", ode_red.poly_ring) - y = str_to_var("y(t)", ode_red.poly_ring) - @test ode_red.x_equations[x] == 9 * x + 12 * y - @test ode_red.x_equations[y] == 4 * x // (y + 16) - end + x = str_to_var("x(t)", ode_red.poly_ring) + y = str_to_var("y(t)", ode_red.poly_ring) + @test ode_red.x_equations[x] == 9 * x + 12 * y + @test ode_red.x_equations[y] == 4 * x // (y + 16) end diff --git a/test/sequence_solution.jl b/test/sequence_solution.jl index 0065d835..5ebeba3b 100644 --- a/test/sequence_solution.jl +++ b/test/sequence_solution.jl @@ -1,27 +1,25 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Sequence solutions in the discrete case" begin - # Fibonacci example - ode = @DDSmodel(f1(t + 1) = f1(t) + f0(t), f0(t + 1) = f1(t), y(t) = f1(t)) +@testset "Sequence solutions in the discrete case" begin + # Fibonacci example + ode = @DDSmodel(f1(t + 1) = f1(t) + f0(t), f0(t + 1) = f1(t), y(t) = f1(t)) - sol = sequence_solution( - ode, - Dict{QQMPolyRingElem, Int}(), - Dict(f0 => 1, f1 => 1), - Dict{QQMPolyRingElem, Array{Int, 1}}(), - 10, - ) - @test sol[f1] == [1, 2, 3, 5, 8, 13, 21, 34, 55, 89] - @test sol[f0] == [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] + sol = sequence_solution( + ode, + Dict{QQMPolyRingElem, Int}(), + Dict(f0 => 1, f1 => 1), + Dict{QQMPolyRingElem, Array{Int, 1}}(), + 10, + ) + @test sol[f1] == [1, 2, 3, 5, 8, 13, 21, 34, 55, 89] + @test sol[f0] == [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] - # Sum of powers - ode = @DDSmodel(a(t + 1) = 2 * a(t) + b * u(t), y(t) = a(t)) - sol = sequence_solution( - ode, - Dict(b => 3), - Dict(a => 1), - Dict(u => [3^i for i in 0:9]), - 10, - ) - @test sol[a] == [-2^i + 3^i for i in 1:10] - end + # Sum of powers + ode = @DDSmodel(a(t + 1) = 2 * a(t) + b * u(t), y(t) = a(t)) + sol = sequence_solution( + ode, + Dict(b => 3), + Dict(a => 1), + Dict(u => [3^i for i in 0:9]), + 10, + ) + @test sol[a] == [-2^i + 3^i for i in 1:10] end diff --git a/test/set_parameter_values.jl b/test/set_parameter_values.jl index 5c2d26ca..046e4a81 100644 --- a/test/set_parameter_values.jl +++ b/test/set_parameter_values.jl @@ -1,21 +1,19 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Set parameter values" begin - sys = @ODEmodel(x'(t) = a * x(t) + a^2, y(t) = x(t)) +@testset "Set parameter values" begin + sys = @ODEmodel(x'(t) = a * x(t) + a^2, y(t) = x(t)) - new_sys = set_parameter_values(sys, Dict(a => Nemo.QQ(1, 2))) - @test evaluate(first(values(new_sys.x_equations)), [2, 2]) == Nemo.QQ(5, 4) + new_sys = set_parameter_values(sys, Dict(a => Nemo.QQ(1, 2))) + @test evaluate(first(values(new_sys.x_equations)), [2, 2]) == Nemo.QQ(5, 4) - new_sys = set_parameter_values(sys, Dict(a => Nemo.QQ(1, 20))) - @test evaluate(first(values(new_sys.x_equations)), [5, 5]) == Nemo.QQ(101, 400) + new_sys = set_parameter_values(sys, Dict(a => Nemo.QQ(1, 20))) + @test evaluate(first(values(new_sys.x_equations)), [5, 5]) == Nemo.QQ(101, 400) - new_sys = set_parameter_values(sys, Dict(a => 0.1)) - @test evaluate(first(values(new_sys.x_equations)), [2, 2]) == Nemo.QQ(21, 100) + new_sys = set_parameter_values(sys, Dict(a => 0.1)) + @test evaluate(first(values(new_sys.x_equations)), [2, 2]) == Nemo.QQ(21, 100) - new_sys = set_parameter_values(sys, Dict(a => 2.1)) - @test evaluate(first(values(new_sys.x_equations)), [2, 2]) == Nemo.QQ(861, 100) + new_sys = set_parameter_values(sys, Dict(a => 2.1)) + @test evaluate(first(values(new_sys.x_equations)), [2, 2]) == Nemo.QQ(861, 100) - sys = @ODEmodel(x'(t) = a + b + c, y(t) = x(t)) - new_sys = set_parameter_values(sys, Dict(a => 2.1, b => 5, c => 3 // 10)) - @test evaluate(first(values(new_sys.x_equations)), [2, 2]) == Nemo.QQ(74, 10) - end + sys = @ODEmodel(x'(t) = a + b + c, y(t) = x(t)) + new_sys = set_parameter_values(sys, Dict(a => 2.1, b => 5, c => 3 // 10)) + @test evaluate(first(values(new_sys.x_equations)), [2, 2]) == Nemo.QQ(74, 10) end diff --git a/test/state_generators.jl b/test/state_generators.jl index 9072f0de..b9ece3e4 100644 --- a/test/state_generators.jl +++ b/test/state_generators.jl @@ -1,34 +1,32 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Generators of observable states" begin - ode = @ODEmodel( - x1'(t) = a * x1(t) - b * x1(t) * x2(t), - x2'(t) = -c * x2(t) + d * x1(t) * x2(t), - y(t) = x1(t) - ) +@testset "Generators of observable states" begin + ode = @ODEmodel( + x1'(t) = a * x1(t) - b * x1(t) * x2(t), + x2'(t) = -c * x2(t) + d * x1(t) * x2(t), + y(t) = x1(t) + ) - sg = states_generators(ode, find_ioequations(ode)) - @test sg == [ - x1 // 1, - (a * x1 - b * x1 * x2) // 1, - ((a - b * x2) * (a * x1 - b * x1 * x2) - b * x1 * (-c * x2 + d * x1 * x2)) // 1, - ] + sg = states_generators(ode, find_ioequations(ode)) + @test sg == [ + x1 // 1, + (a * x1 - b * x1 * x2) // 1, + ((a - b * x2) * (a * x1 - b * x1 * x2) - b * x1 * (-c * x2 + d * x1 * x2)) // 1, + ] - ode = @ODEmodel( - x1'(t) = x2(t), - x2'(t) = x3(t), - x3'(t) = x4(t), - x4'(t) = 1 // x2(t), - y(t) = x1(t) - ) - sg = states_generators(ode, find_ioequations(ode)) - @test sg == [x1 // 1, x2 // 1, x3 // 1, x4 // 1, x2 // 1] + ode = @ODEmodel( + x1'(t) = x2(t), + x2'(t) = x3(t), + x3'(t) = x4(t), + x4'(t) = 1 // x2(t), + y(t) = x1(t) + ) + sg = states_generators(ode, find_ioequations(ode)) + @test sg == [x1 // 1, x2 // 1, x3 // 1, x4 // 1, x2 // 1] - ode = @ODEmodel( - x1'(t) = x2(t), - x2'(t) = (x1(t) + a * x2(t) * u(t)) // (u(t)^2 + x1(t)), - y(t) = x1(t) - ) - sg = states_generators(ode, find_ioequations(ode)) - @test Set(sg) == Set([x1 // 1, x2 // 1, x1 // 1, a * x2 // 1, x1 // 1]) - end + ode = @ODEmodel( + x1'(t) = x2(t), + x2'(t) = (x1(t) + a * x2(t) * u(t)) // (u(t)^2 + x1(t)), + y(t) = x1(t) + ) + sg = states_generators(ode, find_ioequations(ode)) + @test Set(sg) == Set([x1 // 1, x2 // 1, x1 // 1, a * x2 // 1, x1 // 1]) end diff --git a/test/submodels.jl b/test/submodels.jl index 8fcef809..782889cb 100644 --- a/test/submodels.jl +++ b/test/submodels.jl @@ -1,148 +1,146 @@ -if GROUP == "All" || GROUP == "Core" - @testset "Finding submodels" begin - cases = [ - Dict( - :ode => @ODEmodel( - x1'(t) = x1(t)^2, - x2'(t) = x1(t) * x2(t), - y1(t) = x1(t), - y2(t) = x2(t) - ), - :submodels => Set([(["x1(t)"], ["y1(t)"], Vector{String}())]), +@testset "Finding submodels" begin + cases = [ + Dict( + :ode => @ODEmodel( + x1'(t) = x1(t)^2, + x2'(t) = x1(t) * x2(t), + y1(t) = x1(t), + y2(t) = x2(t) ), - Dict( - :ode => @ODEmodel( - x1'(t) = x1(t), - x2'(t) = x2(t) + x1(t), - y1(t) = x1(t), - y2(t) = x1(t)^2, - y3(t) = x2(t) - ), - :submodels => Set([(["x1(t)"], ["y1(t)", "y2(t)"], Vector{String}())]), + :submodels => Set([(["x1(t)"], ["y1(t)"], Vector{String}())]), + ), + Dict( + :ode => @ODEmodel( + x1'(t) = x1(t), + x2'(t) = x2(t) + x1(t), + y1(t) = x1(t), + y2(t) = x1(t)^2, + y3(t) = x2(t) ), - Dict( - :ode => @ODEmodel( - x1'(t) = 0, - x2'(t) = 0, - x3'(t) = 0, - y1(t) = x1(t) + x2(t), - y2(t) = x2(t) + x3(t), - y3(t) = x1(t) + x3(t) - ), - :submodels => Set([ - (["x1(t)", "x2(t)"], ["y1(t)"], Vector{String}()), - (["x2(t)", "x3(t)"], ["y2(t)"], Vector{String}()), - (["x1(t)", "x3(t)"], ["y3(t)"], Vector{String}()), - ]), + :submodels => Set([(["x1(t)"], ["y1(t)", "y2(t)"], Vector{String}())]), + ), + Dict( + :ode => @ODEmodel( + x1'(t) = 0, + x2'(t) = 0, + x3'(t) = 0, + y1(t) = x1(t) + x2(t), + y2(t) = x2(t) + x3(t), + y3(t) = x1(t) + x3(t) ), - Dict( - :ode => @ODEmodel( - x1'(t) = x1(t)^2, - x2'(t) = x1(t) / x2(t), - y1(t) = x1(t), - y2(t) = x2(t) - ), - :submodels => Set([(["x1(t)"], ["y1(t)"], Vector{String}())]), + :submodels => Set([ + (["x1(t)", "x2(t)"], ["y1(t)"], Vector{String}()), + (["x2(t)", "x3(t)"], ["y2(t)"], Vector{String}()), + (["x1(t)", "x3(t)"], ["y3(t)"], Vector{String}()), + ]), + ), + Dict( + :ode => @ODEmodel( + x1'(t) = x1(t)^2, + x2'(t) = x1(t) / x2(t), + y1(t) = x1(t), + y2(t) = x2(t) ), - Dict( - :ode => @ODEmodel( - x1'(t) = x1(t) + a(t) * x2(t)^2, - x2'(t) = x2(t) - b(t) * x1(t) * x2(t), - x3'(t) = x3(t) + x1(t) + x2(t), - y1(t) = d(t) * x1(t), - y2(t) = x3(t) - ), - :submodels => - Set([(["x1(t)", "x2(t)"], ["y1(t)"], ["a(t)", "b(t)", "d(t)"])]), + :submodels => Set([(["x1(t)"], ["y1(t)"], Vector{String}())]), + ), + Dict( + :ode => @ODEmodel( + x1'(t) = x1(t) + a(t) * x2(t)^2, + x2'(t) = x2(t) - b(t) * x1(t) * x2(t), + x3'(t) = x3(t) + x1(t) + x2(t), + y1(t) = d(t) * x1(t), + y2(t) = x3(t) ), - Dict( - :ode => @ODEmodel( - x1'(t) = x1(t), - x2'(t) = x1(t) - c * a(t) * x2(t) * x2(t), - x3'(t) = x3(t) - x1(t) * x2(t) * x3(t), - y1(t) = x1(t), - y2(t) = x1(t) + d(t) * x2(t), - y3(t) = x3(t) - ), - :submodels => Set([ - (["x1(t)", "x2(t)"], ["y1(t)", "y2(t)"], ["a(t)", "d(t)"]), - (["x1(t)"], ["y1(t)"], Vector{String}()), - ]), + :submodels => + Set([(["x1(t)", "x2(t)"], ["y1(t)"], ["a(t)", "b(t)", "d(t)"])]), + ), + Dict( + :ode => @ODEmodel( + x1'(t) = x1(t), + x2'(t) = x1(t) - c * a(t) * x2(t) * x2(t), + x3'(t) = x3(t) - x1(t) * x2(t) * x3(t), + y1(t) = x1(t), + y2(t) = x1(t) + d(t) * x2(t), + y3(t) = x3(t) ), - Dict( - :ode => @ODEmodel( - o1'(t) = r1 * o1(t), - x0'(t) = o1(t) - d * x0(t), - x1'(t) = x0(t) - x1(t), - x2'(t) = x1(t) - x2(t), - x3'(t) = x2(t) - x3(t), - x4'(t) = x3(t) - x4(t), - x5'(t) = x4(t) - x5(t), - o12'(t) = r2 * o12(t), - x02'(t) = o12(t) + x2(t) + x3(t) + x22(t) + x32(t) + x02(t), - x12'(t) = x2(t) + x3(t) + x22(t) + x32(t) + x02(t) - x12(t), - x22'(t) = x12(t) - x22(t), - x32'(t) = x22(t) - x32(t), - x42'(t) = x32(t) - x42(t), - x52'(t) = x42(t) - x52(t), - y0(t) = x0(t) + x02(t), - y1(t) = x1(t) + x12(t), - y2(t) = x2(t) + x22(t), - y3(t) = x3(t) + x32(t), - y4(t) = x4(t) + x42(t) + x5(t) + x52(t), - y5(t) = - x1(t) + x2(t) + x3(t) + x4(t) + x12(t) + x22(t) + x32(t) + x42(t), - y6(t) = x5(t) + x52(t) - ), - :submodels => Set([ - ( - [ - "o1(t)", - "x0(t)", - "x1(t)", - "x2(t)", - "x3(t)", - "o12(t)", - "x02(t)", - "x12(t)", - "x22(t)", - "x32(t)", - ], - ["y0(t)", "y1(t)", "y2(t)", "y3(t)"], - Vector{String}(), - ), - ( - [ - "o1(t)", - "x0(t)", - "x1(t)", - "x2(t)", - "x3(t)", - "x4(t)", - "o12(t)", - "x02(t)", - "x12(t)", - "x22(t)", - "x32(t)", - "x42(t)", - ], - ["y0(t)", "y1(t)", "y2(t)", "y3(t)", "y5(t)"], - Vector{String}(), - ), - ]), + :submodels => Set([ + (["x1(t)", "x2(t)"], ["y1(t)", "y2(t)"], ["a(t)", "d(t)"]), + (["x1(t)"], ["y1(t)"], Vector{String}()), + ]), + ), + Dict( + :ode => @ODEmodel( + o1'(t) = r1 * o1(t), + x0'(t) = o1(t) - d * x0(t), + x1'(t) = x0(t) - x1(t), + x2'(t) = x1(t) - x2(t), + x3'(t) = x2(t) - x3(t), + x4'(t) = x3(t) - x4(t), + x5'(t) = x4(t) - x5(t), + o12'(t) = r2 * o12(t), + x02'(t) = o12(t) + x2(t) + x3(t) + x22(t) + x32(t) + x02(t), + x12'(t) = x2(t) + x3(t) + x22(t) + x32(t) + x02(t) - x12(t), + x22'(t) = x12(t) - x22(t), + x32'(t) = x22(t) - x32(t), + x42'(t) = x32(t) - x42(t), + x52'(t) = x42(t) - x52(t), + y0(t) = x0(t) + x02(t), + y1(t) = x1(t) + x12(t), + y2(t) = x2(t) + x22(t), + y3(t) = x3(t) + x32(t), + y4(t) = x4(t) + x42(t) + x5(t) + x52(t), + y5(t) = + x1(t) + x2(t) + x3(t) + x4(t) + x12(t) + x22(t) + x32(t) + x42(t), + y6(t) = x5(t) + x52(t) ), - ] - - for c in cases - submodels = find_submodels(c[:ode]) - submodels = Set([ + :submodels => Set([ + ( + [ + "o1(t)", + "x0(t)", + "x1(t)", + "x2(t)", + "x3(t)", + "o12(t)", + "x02(t)", + "x12(t)", + "x22(t)", + "x32(t)", + ], + ["y0(t)", "y1(t)", "y2(t)", "y3(t)"], + Vector{String}(), + ), ( - collect(map(var_to_str, ode.x_vars)), - collect(map(var_to_str, ode.y_vars)), - collect(map(var_to_str, ode.u_vars)), - ) for ode in submodels - ]) - @test submodels == c[:submodels] - end + [ + "o1(t)", + "x0(t)", + "x1(t)", + "x2(t)", + "x3(t)", + "x4(t)", + "o12(t)", + "x02(t)", + "x12(t)", + "x22(t)", + "x32(t)", + "x42(t)", + ], + ["y0(t)", "y1(t)", "y2(t)", "y3(t)", "y5(t)"], + Vector{String}(), + ), + ]), + ), + ] + + for c in cases + submodels = find_submodels(c[:ode]) + submodels = Set([ + ( + collect(map(var_to_str, ode.x_vars)), + collect(map(var_to_str, ode.y_vars)), + collect(map(var_to_str, ode.u_vars)), + ) for ode in submodels + ]) + @test submodels == c[:submodels] end end