From ae95c7decbbe263505b99a6744735d44b36a0413 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Thu, 5 Jun 2025 13:04:31 +0530 Subject: [PATCH 1/6] feat: allow passing `wrap_delays` to `build_explicit_observed_function` --- src/systems/diffeqs/odesystem.jl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/systems/diffeqs/odesystem.jl b/src/systems/diffeqs/odesystem.jl index 4a13d7ccf1..334835af58 100644 --- a/src/systems/diffeqs/odesystem.jl +++ b/src/systems/diffeqs/odesystem.jl @@ -476,6 +476,8 @@ Generates a function that computes the observed value(s) `ts` in the system `sys - `throw = true` if true, throw an error when generating a function for `ts` that reference variables that do not exist. - `mkarray`: only used if the output is an array (that is, `!isscalar(ts)` and `ts` is not a tuple, in which case the result will always be a tuple). Called as `mkarray(ts, output_type)` where `ts` are the expressions to put in the array and `output_type` is the argument of the same name passed to build_explicit_observed_function. - `cse = true`: Whether to use Common Subexpression Elimination (CSE) to generate a more efficient function. +- `wrap_delays = is_dde(sys)`: Whether to add an argument for the history function and use + it to calculate all delayed variables. ## Returns @@ -514,7 +516,8 @@ function build_explicit_observed_function(sys, ts; op = Operator, throw = true, cse = true, - mkarray = nothing) + mkarray = nothing, + wrap_delays = is_dde(sys)) is_tuple = ts isa Tuple if is_tuple ts = collect(ts) @@ -600,14 +603,15 @@ function build_explicit_observed_function(sys, ts; p_end = length(dvs) + length(inputs) + length(ps) fns = build_function_wrapper( sys, ts, args...; p_start, p_end, filter_observed = obsfilter, - output_type, mkarray, try_namespaced = true, expression = Val{true}, cse) + output_type, mkarray, try_namespaced = true, expression = Val{true}, cse, + wrap_delays) if fns isa Tuple if expression return return_inplace ? fns : fns[1] end oop, iip = eval_or_rgf.(fns; eval_expression, eval_module) f = GeneratedFunctionWrapper{( - p_start + is_dde(sys), length(args) - length(ps) + 1 + is_dde(sys), is_split(sys))}( + p_start + wrap_delays, length(args) - length(ps) + 1 + wrap_delays, is_split(sys))}( oop, iip) return return_inplace ? (f, f) : f else @@ -616,7 +620,7 @@ function build_explicit_observed_function(sys, ts; end f = eval_or_rgf(fns; eval_expression, eval_module) f = GeneratedFunctionWrapper{( - p_start + is_dde(sys), length(args) - length(ps) + 1 + is_dde(sys), is_split(sys))}( + p_start + wrap_delays, length(args) - length(ps) + 1 + wrap_delays, is_split(sys))}( f, nothing) return f end From c22f2db69029a76fadda793d5388d4949671001e Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Wed, 4 Jun 2025 18:48:33 +0530 Subject: [PATCH 2/6] fix: fix `concrete_getu` for DDEs --- src/systems/problem_utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/systems/problem_utils.jl b/src/systems/problem_utils.jl index f79e8f6286..2f35ec69ad 100644 --- a/src/systems/problem_utils.jl +++ b/src/systems/problem_utils.jl @@ -649,7 +649,7 @@ function. It does NOT work for solutions. """ Base.@nospecializeinfer function concrete_getu(indp, syms::AbstractVector) @nospecialize - obsfn = SymbolicIndexingInterface.observed(indp, syms) + obsfn = build_explicit_observed_function(indp, syms; wrap_delays = false) return ObservedWrapper{is_time_dependent(indp)}(obsfn) end From ca8af6a646b1d2ccb7db0f04a11a5f895bfa7f49 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Thu, 5 Jun 2025 12:57:11 +0530 Subject: [PATCH 3/6] test: fix preface test --- test/odesystem.jl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/test/odesystem.jl b/test/odesystem.jl index 83bb9b3621..6065e9e70f 100644 --- a/test/odesystem.jl +++ b/test/odesystem.jl @@ -5,10 +5,14 @@ using OrdinaryDiffEq, Sundials using DiffEqBase, SparseArrays using StaticArrays using Test -using SymbolicUtils: issym +using SymbolicUtils.Code +using SymbolicUtils: Sym, issym using ForwardDiff using ModelingToolkit: value using ModelingToolkit: t_nounits as t, D_nounits as D +using Symbolics +using Symbolics: unwrap +using DiffEqBase: isinplace # Define some variables @parameters σ ρ β @@ -607,13 +611,6 @@ sys = complete(sys) @test_throws Any ODEFunction(sys) @testset "Preface tests" begin - using OrdinaryDiffEq - using Symbolics - using DiffEqBase: isinplace - using ModelingToolkit - using SymbolicUtils.Code - using SymbolicUtils: Sym - c = [0] function f(c, du::AbstractVector{Float64}, u::AbstractVector{Float64}, p, t::Float64) c .= [c[1] + 1] @@ -656,7 +653,9 @@ sys = complete(sys) @named sys = ODESystem(eqs, t, us, ps; defaults = defs, preface = preface) sys = complete(sys) - prob = ODEProblem(sys, [], (0.0, 1.0)) + # don't build initializeprob because it will use preface in other functions and + # affect `c` + prob = ODEProblem(sys, [], (0.0, 1.0); build_initializeprob = false) sol = solve(prob, Euler(); dt = 0.1) @test c[1] == length(sol) From d77931556bece7fcfb3b400415f1b928a79c2655 Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Thu, 5 Jun 2025 13:14:59 +0530 Subject: [PATCH 4/6] ci: run CI on PRs to backport-v9 --- .github/workflows/Downstream.yml | 2 +- .github/workflows/ReleaseTest.yml | 2 +- .github/workflows/Tests.yml | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Downstream.yml b/.github/workflows/Downstream.yml index 6d79ee746a..0c53a7779e 100644 --- a/.github/workflows/Downstream.yml +++ b/.github/workflows/Downstream.yml @@ -1,7 +1,7 @@ name: IntegrationTest on: push: - branches: [master] + branches: [master, 'backport-v9'] tags: [v*] pull_request: paths-ignore: diff --git a/.github/workflows/ReleaseTest.yml b/.github/workflows/ReleaseTest.yml index f8b592e9d0..96b84f4d79 100644 --- a/.github/workflows/ReleaseTest.yml +++ b/.github/workflows/ReleaseTest.yml @@ -1,7 +1,7 @@ name: ReleaseTest on: push: - branches: [master] + branches: [master, 'backport-v9'] tags: [v*] pull_request: paths-ignore: diff --git a/.github/workflows/Tests.yml b/.github/workflows/Tests.yml index 52c5482970..835ee358a6 100644 --- a/.github/workflows/Tests.yml +++ b/.github/workflows/Tests.yml @@ -5,11 +5,13 @@ on: branches: - master - 'release-' + - 'backport-v9' paths-ignore: - 'docs/**' push: branches: - master + - 'backport-v9' paths-ignore: - 'docs/**' From 5bfb166f7cadc95a39539723663ddd9cae4da18a Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Thu, 5 Jun 2025 14:57:57 +0530 Subject: [PATCH 5/6] fix: fix `ObservedWrapper` for steady state systems --- src/systems/problem_utils.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/systems/problem_utils.jl b/src/systems/problem_utils.jl index 2f35ec69ad..e099ffafbe 100644 --- a/src/systems/problem_utils.jl +++ b/src/systems/problem_utils.jl @@ -631,7 +631,9 @@ end ObservedWrapper{TD}(f::F) where {TD, F} = ObservedWrapper{TD, F}(f) function (ow::ObservedWrapper{true})(prob) - ow.f(state_values(prob), parameter_values(prob), current_time(prob)) + # Edge case for steady state problems + t = applicable(current_time, prob) ? current_time(prob) : Inf + ow.f(state_values(prob), parameter_values(prob), t) end function (ow::ObservedWrapper{false})(prob) From e9b27f9e3a6ef98d15d78cf480caf2459f67a52d Mon Sep 17 00:00:00 2001 From: Aayush Sabharwal Date: Thu, 5 Jun 2025 14:58:25 +0530 Subject: [PATCH 6/6] fix: fix `observed_equations_used_by` for `Initial`s --- src/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils.jl b/src/utils.jl index 90431a7749..6fa574ba35 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1083,7 +1083,7 @@ Keyword arguments: `available_vars` will not be searched for in the observed equations. """ function observed_equations_used_by(sys::AbstractSystem, exprs; - involved_vars = vars(exprs; op = Union{Shift, Differential}), obs = observed(sys), available_vars = []) + involved_vars = vars(exprs; op = Union{Shift, Differential, Initial}), obs = observed(sys), available_vars = []) obsvars = getproperty.(obs, :lhs) graph = observed_dependency_graph(obs) if !(available_vars isa Set)