From baea3814840a525200d50a9f84f8e96c7cccb24a Mon Sep 17 00:00:00 2001 From: vyudu Date: Fri, 17 Jan 2025 11:19:14 -0500 Subject: [PATCH 01/13] add check --- src/systems/diffeqs/odesystem.jl | 1 + src/systems/diffeqs/sdesystem.jl | 1 + src/systems/discrete_system/discrete_system.jl | 1 + src/systems/jumps/jumpsystem.jl | 1 + src/systems/nonlinear/nonlinearsystem.jl | 3 +++ src/systems/optimization/optimizationsystem.jl | 3 +++ src/systems/pde/pdesystem.jl | 3 +++ src/utils.jl | 9 +++++++++ 8 files changed, 22 insertions(+) diff --git a/src/systems/diffeqs/odesystem.jl b/src/systems/diffeqs/odesystem.jl index 61f16fd926..b31f39ec22 100644 --- a/src/systems/diffeqs/odesystem.jl +++ b/src/systems/diffeqs/odesystem.jl @@ -202,6 +202,7 @@ struct ODESystem <: AbstractODESystem check_parameters(ps, iv) check_equations(deqs, iv) check_equations(equations(cevents), iv) + check_var_types(ODESystem, dvs) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ps, iv) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 366e8a6d09..eeedce1d46 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -170,6 +170,7 @@ struct SDESystem <: AbstractODESystem check_parameters(ps, iv) check_equations(deqs, iv) check_equations(neqs, dvs) + check_var_types(SDESystem, dvs) if size(neqs, 1) != length(deqs) throw(ArgumentError("Noise equations ill-formed. Number of rows must match number of drift equations. size(neqs,1) = $(size(neqs,1)) != length(deqs) = $(length(deqs))")) end diff --git a/src/systems/discrete_system/discrete_system.jl b/src/systems/discrete_system/discrete_system.jl index bd5c72eec7..fb6dfe0236 100644 --- a/src/systems/discrete_system/discrete_system.jl +++ b/src/systems/discrete_system/discrete_system.jl @@ -122,6 +122,7 @@ struct DiscreteSystem <: AbstractTimeDependentSystem check_independent_variables([iv]) check_variables(dvs, iv) check_parameters(ps, iv) + check_var_types(DiscreteSystem, dvs) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ps, iv) diff --git a/src/systems/jumps/jumpsystem.jl b/src/systems/jumps/jumpsystem.jl index 5c0a61771a..e5dbb185e2 100644 --- a/src/systems/jumps/jumpsystem.jl +++ b/src/systems/jumps/jumpsystem.jl @@ -147,6 +147,7 @@ struct JumpSystem{U <: ArrayPartition} <: AbstractTimeDependentSystem check_independent_variables([iv]) check_variables(unknowns, iv) check_parameters(ps, iv) + check_var_types(JumpSystem, unknowns) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps, iv) diff --git a/src/systems/nonlinear/nonlinearsystem.jl b/src/systems/nonlinear/nonlinearsystem.jl index 6b0b0cc759..687d55b9bb 100644 --- a/src/systems/nonlinear/nonlinearsystem.jl +++ b/src/systems/nonlinear/nonlinearsystem.jl @@ -115,6 +115,9 @@ struct NonlinearSystem <: AbstractTimeIndependentSystem tearing_state = nothing, substitutions = nothing, complete = false, index_cache = nothing, parent = nothing, isscheduled = false; checks::Union{Bool, Int} = true) + if checks == true || (checks & CheckComponents) > 0 + check_var_types(NonlinearSystem, unknowns) + end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps) check_units(u, eqs) diff --git a/src/systems/optimization/optimizationsystem.jl b/src/systems/optimization/optimizationsystem.jl index 860e063e35..de4696bfa2 100644 --- a/src/systems/optimization/optimizationsystem.jl +++ b/src/systems/optimization/optimizationsystem.jl @@ -73,6 +73,9 @@ struct OptimizationSystem <: AbstractOptimizationSystem gui_metadata = nothing, complete = false, index_cache = nothing, parent = nothing, isscheduled = false; checks::Union{Bool, Int} = true) + if checks == true || (checks & CheckComponents) > 0 + check_var_types(OptimizationSystem, unknowns) + end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps) unwrap(op) isa Symbolic && check_units(u, op) diff --git a/src/systems/pde/pdesystem.jl b/src/systems/pde/pdesystem.jl index eac540e401..e067f0e149 100644 --- a/src/systems/pde/pdesystem.jl +++ b/src/systems/pde/pdesystem.jl @@ -102,6 +102,9 @@ struct PDESystem <: ModelingToolkit.AbstractMultivariateSystem checks::Union{Bool, Int} = true, description = "", name) + if checks == true || (checks & CheckComponents) > 0 + check_var_types(PDESystem, dvs) + end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ivs, ps) check_units(u, eqs) diff --git a/src/utils.jl b/src/utils.jl index 5e4f0b52d2..7324a07115 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -155,6 +155,14 @@ function check_variables(dvs, iv) end end +function check_var_types(sys_type::Type{T}, dvs) where T <: AbstractSystem + any(u -> !(symtype(u) <: Number), dvs) && error("The type of unknown variables must be a numeric type.") + if sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem + any(u -> !(symtype(u) <: Float), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem must be a float.") + end + nothing +end + function check_lhs(eq::Equation, op, dvs::Set) v = unwrap(eq.lhs) _iszero(v) && return @@ -1182,3 +1190,4 @@ function guesses_from_metadata!(guesses, vars) guesses[vars[i]] = varguesses[i] end end + From 74e9d56d083530dfefe86055cca3e5be2f6ab235 Mon Sep 17 00:00:00 2001 From: vyudu Date: Fri, 17 Jan 2025 11:44:57 -0500 Subject: [PATCH 02/13] up --- src/utils.jl | 2 +- test/odesystem.jl | 7 +++++++ test/sdesystem.jl | 9 +++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index 7324a07115..f66055a65c 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -158,7 +158,7 @@ end function check_var_types(sys_type::Type{T}, dvs) where T <: AbstractSystem any(u -> !(symtype(u) <: Number), dvs) && error("The type of unknown variables must be a numeric type.") if sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem - any(u -> !(symtype(u) <: Float), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem must be a float.") + any(u -> !(symtype(u) <: AbstractFloat), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem must be a float.") end nothing end diff --git a/test/odesystem.jl b/test/odesystem.jl index 85d135b338..c354399c3a 100644 --- a/test/odesystem.jl +++ b/test/odesystem.jl @@ -1552,3 +1552,10 @@ end expected_tstops = unique!(sort!(vcat(0.0:0.075:10.0, 0.1, 0.2, 0.65, 0.35, 0.45))) @test all(x -> any(isapprox(x, atol = 1e-6), sol2.t), expected_tstops) end + +let + @parameters p d + @variables X(t)::Int64 + eq = D(X) ~ p - d*X + @test_throws Exception @mtkbuild osys = ODESystem([eq], t) +end diff --git a/test/sdesystem.jl b/test/sdesystem.jl index 8069581dcc..95a5358db6 100644 --- a/test/sdesystem.jl +++ b/test/sdesystem.jl @@ -868,3 +868,12 @@ end @test length(ModelingToolkit.get_noiseeqs(sys)) == 1 @test length(observed(sys)) == 1 end + +# Test validating types of states +let + @parameters p d + @variables X(t)::Int64 + @brownian z + eq2 = D(X) ~ p - d*X + z + @test_throws Exception @mtkbuild ssys = System([eq2], t) +end From 557b01fc6539b0ca09c8a3ffcad271ba74760287 Mon Sep 17 00:00:00 2001 From: vyudu Date: Fri, 17 Jan 2025 14:50:56 -0500 Subject: [PATCH 03/13] fix --- src/utils.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index f66055a65c..4742ed819f 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -156,9 +156,12 @@ function check_variables(dvs, iv) end function check_var_types(sys_type::Type{T}, dvs) where T <: AbstractSystem - any(u -> !(symtype(u) <: Number), dvs) && error("The type of unknown variables must be a numeric type.") - if sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem - any(u -> !(symtype(u) <: AbstractFloat), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem must be a float.") + if any(u -> !(symtype(u) <: Number), dvs) + error("The type of unknown variables must be a numeric type.") + elseif any(u -> (symtype(u) !== symtype(dvs[1])), dvs) + error("The type of all the unknown variables in a system must all be the same.") + elseif sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem + any(u -> !(symtype(u) == Real), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem should not be a concrete numeric type.") end nothing end From e12313e4201f878aa09b502d1dee2c79a9bb8130 Mon Sep 17 00:00:00 2001 From: vyudu Date: Fri, 17 Jan 2025 15:13:46 -0500 Subject: [PATCH 04/13] handle arr --- src/utils.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 4742ed819f..736a4cc03e 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -156,12 +156,12 @@ function check_variables(dvs, iv) end function check_var_types(sys_type::Type{T}, dvs) where T <: AbstractSystem - if any(u -> !(symtype(u) <: Number), dvs) + if any(u -> !(symtype(u) <: Number || eltype(symtype(u)) <: Number), dvs) error("The type of unknown variables must be a numeric type.") - elseif any(u -> (symtype(u) !== symtype(dvs[1])), dvs) + elseif any(u -> (symtype(u) !== symtype(dvs[1])), dvs) error("The type of all the unknown variables in a system must all be the same.") elseif sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem - any(u -> !(symtype(u) == Real), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem should not be a concrete numeric type.") + any(u -> !(symtype(u) == Real || eltype(symtype(u)) == Real), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem should not be a concrete numeric type.") end nothing end From 45c2588e6e65025283b97dd473e0052c7ed6bb66 Mon Sep 17 00:00:00 2001 From: vyudu Date: Thu, 23 Jan 2025 13:40:11 -0500 Subject: [PATCH 05/13] fix pdesystem typecheck --- src/systems/pde/pdesystem.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/pde/pdesystem.jl b/src/systems/pde/pdesystem.jl index e067f0e149..e2a5bb5734 100644 --- a/src/systems/pde/pdesystem.jl +++ b/src/systems/pde/pdesystem.jl @@ -103,7 +103,7 @@ struct PDESystem <: ModelingToolkit.AbstractMultivariateSystem description = "", name) if checks == true || (checks & CheckComponents) > 0 - check_var_types(PDESystem, dvs) + check_var_types(PDESystem, [dv(ivs...) for dv in dvs]) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ivs, ps) From eea66d053b5b0ad52c180d9662165e0060a3345c Mon Sep 17 00:00:00 2001 From: vyudu Date: Wed, 29 Jan 2025 09:47:01 -0500 Subject: [PATCH 06/13] up --- src/utils.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index a83ba2fde7..f956c90a67 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -156,11 +156,11 @@ function check_variables(dvs, iv) end function check_var_types(sys_type::Type{T}, dvs) where T <: AbstractSystem - if any(u -> !(symtype(u) <: Number || eltype(symtype(u)) <: Number), dvs) - error("The type of unknown variables must be a numeric type.") - elseif any(u -> (eltype(symtype(u)) !== eltype(symtype(dvs[1]))), dvs) - error("The element type of all the unknown variables in a system must all be the same.") - elseif sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem + # if any(u -> !(symtype(u) <: Number || eltype(symtype(u)) <: Number), dvs) + # error("The type of unknown variables must be a numeric type.") + # elseif any(u -> (eltype(symtype(u)) !== eltype(symtype(dvs[1]))), dvs) + # error("The element type of all the unknown variables in a system must all be the same.") + if sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem any(u -> !(symtype(u) == Real || eltype(symtype(u)) == Real), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem should not be a concrete numeric type.") end nothing From a9b7fa143b2b3dcfcab5b266d2131b479daf83ce Mon Sep 17 00:00:00 2001 From: vyudu Date: Wed, 29 Jan 2025 10:42:10 -0500 Subject: [PATCH 07/13] up --- src/systems/diffeqs/odesystem.jl | 2 ++ src/utils.jl | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/systems/diffeqs/odesystem.jl b/src/systems/diffeqs/odesystem.jl index 8edbb46cf1..4a3a172d60 100644 --- a/src/systems/diffeqs/odesystem.jl +++ b/src/systems/diffeqs/odesystem.jl @@ -329,6 +329,8 @@ function ODESystem(eqs, iv; kwargs...) throw(ArgumentError("The differential variable $diffvar is not unique in the system of equations.")) push!(diffvars, diffvar) end + !(symtype(diffvar) === Real || eltype(symtype(var)) === Real) && throw(ArgumentError("Differential variable $var has type $(symtype(var)). Differential variables should not be concretely typed.")) + push!(diffeq, eq) else push!(algeeq, eq) diff --git a/src/utils.jl b/src/utils.jl index 8691111b01..34058818c8 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -161,9 +161,10 @@ function check_var_types(sys_type::Type{T}, dvs) where T <: AbstractSystem # elseif any(u -> (eltype(symtype(u)) !== eltype(symtype(dvs[1]))), dvs) # error("The element type of all the unknown variables in a system must all be the same.") if sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem - any(u -> !(symtype(u) == Real || eltype(symtype(u)) == Real), dvs) && error("The type of unknown variables for an SDESystem, PDESystem, or ODESystem should not be a concrete numeric type.") + for var in dvs + !(symtype(var) === Real || eltype(symtype(var)) === Real) && throw(ArgumentError("Differential variable $var has type $(symtype(var)). The type of unknown variables for an SDESystem, PDESystem, or ODESystem should be Real.")) + end end - nothing end function check_lhs(eq::Equation, op, dvs::Set) From 68bb1b3a3543bc1c3b340cb0bd9d72941de7d9b7 Mon Sep 17 00:00:00 2001 From: vyudu Date: Wed, 29 Jan 2025 11:53:30 -0500 Subject: [PATCH 08/13] up --- src/systems/diffeqs/odesystem.jl | 1 - src/systems/diffeqs/sdesystem.jl | 1 - src/utils.jl | 12 ------------ 3 files changed, 14 deletions(-) diff --git a/src/systems/diffeqs/odesystem.jl b/src/systems/diffeqs/odesystem.jl index 4a3a172d60..32169c1ecd 100644 --- a/src/systems/diffeqs/odesystem.jl +++ b/src/systems/diffeqs/odesystem.jl @@ -202,7 +202,6 @@ struct ODESystem <: AbstractODESystem check_parameters(ps, iv) check_equations(deqs, iv) check_equations(equations(cevents), iv) - check_var_types(ODESystem, dvs) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ps, iv) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 69e984d4ce..df950ee01c 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -170,7 +170,6 @@ struct SDESystem <: AbstractODESystem check_parameters(ps, iv) check_equations(deqs, iv) check_equations(neqs, dvs) - check_var_types(SDESystem, dvs) if size(neqs, 1) != length(deqs) throw(ArgumentError("Noise equations ill-formed. Number of rows must match number of drift equations. size(neqs,1) = $(size(neqs,1)) != length(deqs) = $(length(deqs))")) end diff --git a/src/utils.jl b/src/utils.jl index 34058818c8..29b9e424ac 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -155,18 +155,6 @@ function check_variables(dvs, iv) end end -function check_var_types(sys_type::Type{T}, dvs) where T <: AbstractSystem - # if any(u -> !(symtype(u) <: Number || eltype(symtype(u)) <: Number), dvs) - # error("The type of unknown variables must be a numeric type.") - # elseif any(u -> (eltype(symtype(u)) !== eltype(symtype(dvs[1]))), dvs) - # error("The element type of all the unknown variables in a system must all be the same.") - if sys_type == ODESystem || sys_type == SDESystem || sys_type == PDESystem - for var in dvs - !(symtype(var) === Real || eltype(symtype(var)) === Real) && throw(ArgumentError("Differential variable $var has type $(symtype(var)). The type of unknown variables for an SDESystem, PDESystem, or ODESystem should be Real.")) - end - end -end - function check_lhs(eq::Equation, op, dvs::Set) v = unwrap(eq.lhs) _iszero(v) && return From 5c1cebf0658d64328a353d1beb7913d535590480 Mon Sep 17 00:00:00 2001 From: vyudu Date: Wed, 29 Jan 2025 13:09:29 -0500 Subject: [PATCH 09/13] add constructor --- src/systems/diffeqs/odesystem.jl | 67 ++----------------------------- src/systems/diffeqs/sdesystem.jl | 15 +++++++ src/utils.jl | 69 ++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 64 deletions(-) diff --git a/src/systems/diffeqs/odesystem.jl b/src/systems/diffeqs/odesystem.jl index 41bcaf1d01..efd7509572 100644 --- a/src/systems/diffeqs/odesystem.jl +++ b/src/systems/diffeqs/odesystem.jl @@ -296,71 +296,10 @@ function ODESystem(deqs::AbstractVector{<:Equation}, iv, dvs, ps; end function ODESystem(eqs, iv; kwargs...) - eqs = collect(eqs) - # NOTE: this assumes that the order of algebraic equations doesn't matter - diffvars = OrderedSet() - allunknowns = OrderedSet() - ps = OrderedSet() - # reorder equations such that it is in the form of `diffeq, algeeq` - diffeq = Equation[] - algeeq = Equation[] - # initial loop for finding `iv` - if iv === nothing - for eq in eqs - if !(eq.lhs isa Number) # assume eq.lhs is either Differential or Number - iv = iv_from_nested_derivative(eq.lhs) - break - end - end - end - iv = value(iv) - iv === nothing && throw(ArgumentError("Please pass in independent variables.")) - compressed_eqs = Equation[] # equations that need to be expanded later, like `connect(a, b)` - for eq in eqs - eq.lhs isa Union{Symbolic, Number} || (push!(compressed_eqs, eq); continue) - collect_vars!(allunknowns, ps, eq, iv) - if isdiffeq(eq) - diffvar, _ = var_from_nested_derivative(eq.lhs) - if check_scope_depth(getmetadata(diffvar, SymScope, LocalScope()), 0) - isequal(iv, iv_from_nested_derivative(eq.lhs)) || - throw(ArgumentError("An ODESystem can only have one independent variable.")) - diffvar in diffvars && - throw(ArgumentError("The differential variable $diffvar is not unique in the system of equations.")) - push!(diffvars, diffvar) - end - push!(diffeq, eq) - else - push!(algeeq, eq) - end - end - for eq in get(kwargs, :parameter_dependencies, Equation[]) - collect_vars!(allunknowns, ps, eq, iv) - end - for ssys in get(kwargs, :systems, ODESystem[]) - collect_scoped_vars!(allunknowns, ps, ssys, iv) - end - for v in allunknowns - isdelay(v, iv) || continue - collect_vars!(allunknowns, ps, arguments(v)[1], iv) - end - new_ps = OrderedSet() - for p in ps - if iscall(p) && operation(p) === getindex - par = arguments(p)[begin] - if Symbolics.shape(Symbolics.unwrap(par)) !== Symbolics.Unknown() && - all(par[i] in ps for i in eachindex(par)) - push!(new_ps, par) - else - push!(new_ps, p) - end - else - push!(new_ps, p) - end - end - algevars = setdiff(allunknowns, diffvars) + param_deps = get(kwargs, :parameter_dependencies, Equation[]) + eqs, dvs, ps = process_equations_DESystem(eqs, param_deps, iv) # the orders here are very important! - return ODESystem(Equation[diffeq; algeeq; compressed_eqs], iv, - collect(Iterators.flatten((diffvars, algevars))), collect(new_ps); kwargs...) + return ODESystem(eqs, iv, dvs, ps; kwargs...) end # NOTE: equality does not check cached Jacobian diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index df950ee01c..0462d4d12d 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -273,6 +273,21 @@ function SDESystem(sys::ODESystem, neqs; kwargs...) SDESystem(equations(sys), neqs, get_iv(sys), unknowns(sys), parameters(sys); kwargs...) end +function SDESystem(eqs, noiseeqs, iv; kwargs...) + param_deps = get(kwargs, :parameter_dependencies, Equation[]) + eqs, dvs, ps = process_equations_DESystem(eqs, param_deps, iv) + + # validate noise equations + noisedvs = OrderedSet() + noiseps = OrderedSet() + collect_vars!(noisedvs, noiseps, noiseeqs, iv) + for dv in noisedvs + var ∈ Set(dvs) || throw(ArgumentError("Variable $dv in noise equations is not an unknown of the system.")) + end + + return SDESystem(eqs, noiseeqs, iv, dvs, [ps; collect(noiseps)]; kwargs...) +end + function Base.:(==)(sys1::SDESystem, sys2::SDESystem) sys1 === sys2 && return true iv1 = get_iv(sys1) diff --git a/src/utils.jl b/src/utils.jl index b6544972b9..9cb109c1d3 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1185,3 +1185,72 @@ function guesses_from_metadata!(guesses, vars) guesses[vars[i]] = varguesses[i] end end + +""" + $(TYPEDSIGNATURES) + +Find all the unknowns and parameters from the equations and parameter dependencies of an ODESystem or SDESystem. Return re-ordered equations, unknowns, and parameters. +""" +function process_equations_DESystem(eqs, param_deps, iv) + eqs = collect(eqs) + + diffvars = OrderedSet() + allunknowns = OrderedSet() + ps = OrderedSet() + + # NOTE: this assumes that the order of algebraic equations doesn't matter + # reorder equations such that it is in the form of `diffeq, algeeq` + diffeq = Equation[] + algeeq = Equation[] + # initial loop for finding `iv` + if iv === nothing + for eq in eqs + if !(eq.lhs isa Number) # assume eq.lhs is either Differential or Number + iv = iv_from_nested_derivative(eq.lhs) + break + end + end + end + iv = value(iv) + iv === nothing && throw(ArgumentError("Please pass in independent variables.")) + + compressed_eqs = Equation[] # equations that need to be expanded later, like `connect(a, b)` + for eq in eqs + eq.lhs isa Union{Symbolic, Number} || (push!(compressed_eqs, eq); continue) + collect_vars!(allunknowns, ps, eq, iv) + if isdiffeq(eq) + diffvar, _ = var_from_nested_derivative(eq.lhs) + if check_scope_depth(getmetadata(diffvar, SymScope, LocalScope()), 0) + isequal(iv, iv_from_nested_derivative(eq.lhs)) || + throw(ArgumentError("An ODESystem can only have one independent variable.")) + diffvar in diffvars && + throw(ArgumentError("The differential variable $diffvar is not unique in the system of equations.")) + push!(diffvars, diffvar) + end + push!(diffeq, eq) + else + push!(algeeq, eq) + end + end + for eq in param_deps + collect_vars!(allunknowns, ps, eq, iv) + end + + new_ps = OrderedSet() + for p in ps + if iscall(p) && operation(p) === getindex + par = arguments(p)[begin] + if Symbolics.shape(Symbolics.unwrap(par)) !== Symbolics.Unknown() && + all(par[i] in ps for i in eachindex(par)) + push!(new_ps, par) + else + push!(new_ps, p) + end + else + push!(new_ps, p) + end + end + algevars = setdiff(allunknowns, diffvars) + + Equation[diffeq; algeeq; compressed_eqs], collect(Iterators.flatten((diffvars, algevars))), collect(new_ps) +end From 3ea2dd389e425c9adb26545cd4ca3428fc48c3d4 Mon Sep 17 00:00:00 2001 From: vyudu Date: Wed, 29 Jan 2025 13:15:42 -0500 Subject: [PATCH 10/13] remove check_var_type --- src/systems/discrete_system/discrete_system.jl | 1 - src/systems/jumps/jumpsystem.jl | 1 - src/systems/nonlinear/nonlinearsystem.jl | 3 --- src/systems/optimization/optimizationsystem.jl | 3 --- src/systems/pde/pdesystem.jl | 3 --- 5 files changed, 11 deletions(-) diff --git a/src/systems/discrete_system/discrete_system.jl b/src/systems/discrete_system/discrete_system.jl index fb6dfe0236..bd5c72eec7 100644 --- a/src/systems/discrete_system/discrete_system.jl +++ b/src/systems/discrete_system/discrete_system.jl @@ -122,7 +122,6 @@ struct DiscreteSystem <: AbstractTimeDependentSystem check_independent_variables([iv]) check_variables(dvs, iv) check_parameters(ps, iv) - check_var_types(DiscreteSystem, dvs) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ps, iv) diff --git a/src/systems/jumps/jumpsystem.jl b/src/systems/jumps/jumpsystem.jl index 4d77f4106d..f39b5fa46a 100644 --- a/src/systems/jumps/jumpsystem.jl +++ b/src/systems/jumps/jumpsystem.jl @@ -147,7 +147,6 @@ struct JumpSystem{U <: ArrayPartition} <: AbstractTimeDependentSystem check_independent_variables([iv]) check_variables(unknowns, iv) check_parameters(ps, iv) - check_var_types(JumpSystem, unknowns) end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps, iv) diff --git a/src/systems/nonlinear/nonlinearsystem.jl b/src/systems/nonlinear/nonlinearsystem.jl index 41aaf65d1a..89663f7dbc 100644 --- a/src/systems/nonlinear/nonlinearsystem.jl +++ b/src/systems/nonlinear/nonlinearsystem.jl @@ -115,9 +115,6 @@ struct NonlinearSystem <: AbstractTimeIndependentSystem tearing_state = nothing, substitutions = nothing, complete = false, index_cache = nothing, parent = nothing, isscheduled = false; checks::Union{Bool, Int} = true) - if checks == true || (checks & CheckComponents) > 0 - check_var_types(NonlinearSystem, unknowns) - end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps) check_units(u, eqs) diff --git a/src/systems/optimization/optimizationsystem.jl b/src/systems/optimization/optimizationsystem.jl index de4696bfa2..860e063e35 100644 --- a/src/systems/optimization/optimizationsystem.jl +++ b/src/systems/optimization/optimizationsystem.jl @@ -73,9 +73,6 @@ struct OptimizationSystem <: AbstractOptimizationSystem gui_metadata = nothing, complete = false, index_cache = nothing, parent = nothing, isscheduled = false; checks::Union{Bool, Int} = true) - if checks == true || (checks & CheckComponents) > 0 - check_var_types(OptimizationSystem, unknowns) - end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(unknowns, ps) unwrap(op) isa Symbolic && check_units(u, op) diff --git a/src/systems/pde/pdesystem.jl b/src/systems/pde/pdesystem.jl index e2a5bb5734..eac540e401 100644 --- a/src/systems/pde/pdesystem.jl +++ b/src/systems/pde/pdesystem.jl @@ -102,9 +102,6 @@ struct PDESystem <: ModelingToolkit.AbstractMultivariateSystem checks::Union{Bool, Int} = true, description = "", name) - if checks == true || (checks & CheckComponents) > 0 - check_var_types(PDESystem, [dv(ivs...) for dv in dvs]) - end if checks == true || (checks & CheckUnits) > 0 u = __get_unit_type(dvs, ivs, ps) check_units(u, eqs) From 3329ce5e838177fcd4a369bf73e0e6dfd837aedf Mon Sep 17 00:00:00 2001 From: vyudu Date: Wed, 29 Jan 2025 13:29:36 -0500 Subject: [PATCH 11/13] up --- src/systems/diffeqs/sdesystem.jl | 5 ++++- src/utils.jl | 2 +- test/odesystem.jl | 5 ++++- test/sdesystem.jl | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 0462d4d12d..747d8e844c 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -273,7 +273,7 @@ function SDESystem(sys::ODESystem, neqs; kwargs...) SDESystem(equations(sys), neqs, get_iv(sys), unknowns(sys), parameters(sys); kwargs...) end -function SDESystem(eqs, noiseeqs, iv; kwargs...) +function SDESystem(eqs::Vector{Equation}, noiseeqs::AbstractArray, iv; kwargs...) param_deps = get(kwargs, :parameter_dependencies, Equation[]) eqs, dvs, ps = process_equations_DESystem(eqs, param_deps, iv) @@ -288,6 +288,9 @@ function SDESystem(eqs, noiseeqs, iv; kwargs...) return SDESystem(eqs, noiseeqs, iv, dvs, [ps; collect(noiseps)]; kwargs...) end +SDESystem(eq::Equation, noiseeqs::AbstractArray, args...; kwargs...) = SDESystem([eq], noiseeqs, args...; kwargs...) +SDESystem(eq::Equation, noiseeq, args...; kwargs...) = SDESystem([eq], [noiseeq], args...; kwargs...) + function Base.:(==)(sys1::SDESystem, sys2::SDESystem) sys1 === sys2 && return true iv1 = get_iv(sys1) diff --git a/src/utils.jl b/src/utils.jl index f3828bea4f..a71e9ba799 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1225,7 +1225,7 @@ function process_equations_DESystem(eqs, param_deps, iv) throw(ArgumentError("An ODESystem can only have one independent variable.")) diffvar in diffvars && throw(ArgumentError("The differential variable $diffvar is not unique in the system of equations.")) - !(symtype(diffvar) === Real || eltype(symtype(diffvar)) === Real) && throw(ArgumentError("Differential variable $var has type $(symtype(diffvar)). Differential variables should not be concretely typed.")) + !(symtype(diffvar) === Real || eltype(symtype(diffvar)) === Real) && throw(ArgumentError("Differential variable $diffvar has type $(symtype(diffvar)). Differential variables should not be concretely typed.")) push!(diffvars, diffvar) end push!(diffeq, eq) diff --git a/test/odesystem.jl b/test/odesystem.jl index 29076416fa..dce9275efe 100644 --- a/test/odesystem.jl +++ b/test/odesystem.jl @@ -1548,7 +1548,10 @@ end @parameters p d @variables X(t)::Int64 eq = D(X) ~ p - d*X - @test_throws Exception @mtkbuild osys = ODESystem([eq], t) + @test_throws ArgumentError @mtkbuild osys = ODESystem([eq], t) + @variables Y(t)[1:3]::String + eq = D(Y) ~ [p, p, p] + @test_throws ArgumentError @mtkbuild osys = ODESystem([eq], t) end # Test `isequal` diff --git a/test/sdesystem.jl b/test/sdesystem.jl index dee497f62c..7cac3645c4 100644 --- a/test/sdesystem.jl +++ b/test/sdesystem.jl @@ -875,7 +875,9 @@ end @variables X(t)::Int64 @brownian z eq2 = D(X) ~ p - d*X + z - @test_throws Exception @mtkbuild ssys = System([eq2], t) + @test_throws ArgumentError @mtkbuild ssys = System([eq2], t) + noiseeq = [1] + @test_throws ArgumentError @named ssys = SDESystem([eq2], [noiseeq], t) end @testset "SDEFunctionExpr" begin From 288a4a01030df20a9f106012c0bfcea52a1632fc Mon Sep 17 00:00:00 2001 From: vyudu Date: Wed, 29 Jan 2025 14:01:23 -0500 Subject: [PATCH 12/13] up --- src/systems/diffeqs/odesystem.jl | 35 ++++++++++++++++++++++++++++--- src/systems/diffeqs/sdesystem.jl | 36 ++++++++++++++++++++++++++++---- src/utils.jl | 25 +++------------------- 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/src/systems/diffeqs/odesystem.jl b/src/systems/diffeqs/odesystem.jl index 4aaf79f3f1..9953e28428 100644 --- a/src/systems/diffeqs/odesystem.jl +++ b/src/systems/diffeqs/odesystem.jl @@ -296,9 +296,38 @@ function ODESystem(deqs::AbstractVector{<:Equation}, iv, dvs, ps; end function ODESystem(eqs, iv; kwargs...) - param_deps = get(kwargs, :parameter_dependencies, Equation[]) - eqs, dvs, ps = process_equations_DESystem(eqs, param_deps, iv) - return ODESystem(eqs, iv, dvs, ps; kwargs...) + diffvars, allunknowns, ps, eqs = process_equations(eqs, iv) + + for eq in get(kwargs, :parameter_dependencies, Equation[]) + collect_vars!(allunknowns, ps, eq, iv) + end + + for ssys in get(kwargs, :systems, ODESystem[]) + collect_scoped_vars!(allunknowns, ps, ssys, iv) + end + + for v in allunknowns + isdelay(v, iv) || continue + collect_vars!(allunknowns, ps, arguments(v)[1], iv) + end + + new_ps = OrderedSet() + for p in ps + if iscall(p) && operation(p) === getindex + par = arguments(p)[begin] + if Symbolics.shape(Symbolics.unwrap(par)) !== Symbolics.Unknown() && + all(par[i] in ps for i in eachindex(par)) + push!(new_ps, par) + else + push!(new_ps, p) + end + else + push!(new_ps, p) + end + end + algevars = setdiff(allunknowns, diffvars) + + return ODESystem(eqs, iv, collect(Iterators.flatten((diffvars, algevars))), collect(new_ps); kwargs...) end # NOTE: equality does not check cached Jacobian diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 747d8e844c..0d0a2ec0c7 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -274,18 +274,46 @@ function SDESystem(sys::ODESystem, neqs; kwargs...) end function SDESystem(eqs::Vector{Equation}, noiseeqs::AbstractArray, iv; kwargs...) - param_deps = get(kwargs, :parameter_dependencies, Equation[]) - eqs, dvs, ps = process_equations_DESystem(eqs, param_deps, iv) + diffvars, allunknowns, ps, eqs = process_equations(eqs, iv) + + for eq in get(kwargs, :parameter_dependencies, Equation[]) + collect_vars!(allunknowns, ps, eq, iv) + end + + for ssys in get(kwargs, :systems, ODESystem[]) + collect_scoped_vars!(allunknowns, ps, ssys, iv) + end + + for v in allunknowns + isdelay(v, iv) || continue + collect_vars!(allunknowns, ps, arguments(v)[1], iv) + end + + new_ps = OrderedSet() + for p in ps + if iscall(p) && operation(p) === getindex + par = arguments(p)[begin] + if Symbolics.shape(Symbolics.unwrap(par)) !== Symbolics.Unknown() && + all(par[i] in ps for i in eachindex(par)) + push!(new_ps, par) + else + push!(new_ps, p) + end + else + push!(new_ps, p) + end + end # validate noise equations noisedvs = OrderedSet() noiseps = OrderedSet() collect_vars!(noisedvs, noiseps, noiseeqs, iv) for dv in noisedvs - var ∈ Set(dvs) || throw(ArgumentError("Variable $dv in noise equations is not an unknown of the system.")) + var ∈ allunknowns || throw(ArgumentError("Variable $dv in noise equations is not an unknown of the system.")) end + algevars = setdiff(allunknowns, diffvars) - return SDESystem(eqs, noiseeqs, iv, dvs, [ps; collect(noiseps)]; kwargs...) + return SDESystem(eqs, noiseeqs, iv, Iterators.flatten((diffvars, algevars)), [ps; collect(noiseps)]; kwargs...) end SDESystem(eq::Equation, noiseeqs::AbstractArray, args...; kwargs...) = SDESystem([eq], noiseeqs, args...; kwargs...) diff --git a/src/utils.jl b/src/utils.jl index a71e9ba799..7bb1ebcbe9 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1189,9 +1189,9 @@ end """ $(TYPEDSIGNATURES) -Find all the unknowns and parameters from the equations and parameter dependencies of an ODESystem or SDESystem. Return re-ordered equations, unknowns, and parameters. +Find all the unknowns and parameters from the equations of a SDESystem or ODESystem. Return re-ordered equations, differential variables, all variables, and parameters. """ -function process_equations_DESystem(eqs, param_deps, iv) +function process_equations(eqs, iv) eqs = collect(eqs) diffvars = OrderedSet() @@ -1233,25 +1233,6 @@ function process_equations_DESystem(eqs, param_deps, iv) push!(algeeq, eq) end end - for eq in param_deps - collect_vars!(allunknowns, ps, eq, iv) - end - - new_ps = OrderedSet() - for p in ps - if iscall(p) && operation(p) === getindex - par = arguments(p)[begin] - if Symbolics.shape(Symbolics.unwrap(par)) !== Symbolics.Unknown() && - all(par[i] in ps for i in eachindex(par)) - push!(new_ps, par) - else - push!(new_ps, p) - end - else - push!(new_ps, p) - end - end - algevars = setdiff(allunknowns, diffvars) - Equation[diffeq; algeeq; compressed_eqs], collect(Iterators.flatten((diffvars, algevars))), collect(new_ps) + diffvars, allunknowns, ps, Equation[diffeq; algeeq; compressed_eqs] end From 84759d821283c3ff51c34600350a03605bdbf7d9 Mon Sep 17 00:00:00 2001 From: vyudu Date: Wed, 29 Jan 2025 14:06:16 -0500 Subject: [PATCH 13/13] Fix typo --- src/systems/diffeqs/sdesystem.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/diffeqs/sdesystem.jl b/src/systems/diffeqs/sdesystem.jl index 0d0a2ec0c7..f82bc5cadc 100644 --- a/src/systems/diffeqs/sdesystem.jl +++ b/src/systems/diffeqs/sdesystem.jl @@ -309,7 +309,7 @@ function SDESystem(eqs::Vector{Equation}, noiseeqs::AbstractArray, iv; kwargs... noiseps = OrderedSet() collect_vars!(noisedvs, noiseps, noiseeqs, iv) for dv in noisedvs - var ∈ allunknowns || throw(ArgumentError("Variable $dv in noise equations is not an unknown of the system.")) + dv ∈ allunknowns || throw(ArgumentError("Variable $dv in noise equations is not an unknown of the system.")) end algevars = setdiff(allunknowns, diffvars)