Skip to content

Symbolic observed variables in DDE solutions fail at interpolated times #1527

Description

@Fololo

Describe the bug 🐞

Evaluating an observed variable of a non-Markovian ModelingToolkit system over an interpolated vector of times throws a MethodError because SymbolicIndexingInterface.get_history_function is called with a RecursiveArrayTools.DiffEqArray.

The saved solution values can be indexed successfully (sol[sys.y]). The failure occurs only when requesting interpolated values, including through the standard plotting recipe (plot(sol; idxs = sys.y)).

Expected behavior

A symbolic observed variable of a DDEProblem should evaluate successfully at interpolated times and therefore be plottable with plot(sol; idxs = sys.y).

The full AbstractODESolution has a get_history_function implementation, and the observed-function evaluation should retain access to that history when it evaluates a non-Markovian system over interpolated states.

Minimal Reproducible Example 👇

using ModelingToolkit
using DelayDiffEq
using OrdinaryDiffEq
using Plots

@parameters t τ = 1.0
@variables x(..) y(t)

x_t = x(t)
D = Differential(t)

sys = mtkcompile(System(
    [
        D(x_t) ~ -x_t + x(t - τ),
        y ~ x_t,
    ],
    t;
    name = :delay_mre,
))

prob = DDEProblem(
    sys,
    Dict(x_t => 1.0),
    (0.0, 2.0);
    h = (p, time) -> [1.0],
    build_initializeprob = false,
)

sol = solve(prob, MethodOfSteps(Tsit5()))

sol[sys.y]  # succeeds: evaluates at saved solution times

times = collect(range(0.0, 2.0; length = 11))
sol(times; idxs = sys.y)  # throws MethodError

plot(sol; idxs = sys.y)  # throws the same MethodError

Error & Stacktrace ⚠️

ERROR: MethodError: no method matching get_history_function(::RecursiveArrayTools.DiffEqArray{…})

Closest candidates are:
  get_history_function(::DelayDiffEq.DDEIntegrator)
  get_history_function(::SciMLBase.AbstractDDEProblem)
  get_history_function(::SciMLBase.AbstractSDDEProblem)
  get_history_function(::SciMLBase.AbstractODESolution)

Stacktrace:
 [1] (::SymbolicIndexingInterface.TimeDependentObservedFunction{…})(
       ::SymbolicIndexingInterface.Timeseries,
       ::SymbolicIndexingInterface.IndexerBoth,
       prob::RecursiveArrayTools.DiffEqArray{…})
     @ SymbolicIndexingInterface ~/.julia/packages/SymbolicIndexingInterface/qKPnc/src/state_indexing.jl:99
 [2] (::SymbolicIndexingInterface.TimeDependentObservedFunction{…})(
       ::SymbolicIndexingInterface.Timeseries,
       prob::RecursiveArrayTools.DiffEqArray{…})
     @ SymbolicIndexingInterface ~/.julia/packages/SymbolicIndexingInterface/qKPnc/src/state_indexing.jl:88
 [3] (::SciMLBase.AbstractODESolution)(
       t::Vector{Float64}, ::Type{Val{0}}, idxs::Num, continuity::Symbol)
     @ SciMLBase ~/.julia/packages/SciMLBase/itmhi/src/solutions/ode_solutions.jl:398

Environment (please complete the following information):

  • Output of using Pkg; Pkg.status()
Status `~/Documents/julia_dcm/docs/Project.toml`
  [bcd4f6db] DelayDiffEq v6.1.2
  [31c24e10] Distributions v0.25.130
  [e30172f5] Documenter v1.17.0
  [363c841f] JuliaDCM v0.1.0 `.`
  [961ee093] ModelingToolkit v11.39.0
  [1dea7af3] OrdinaryDiffEq v7.6.0
  [91a5bcdd] Plots v1.41.6
  • Output of using Pkg; Pkg.status(; mode = PKGMODE_MANIFEST)
Status `~/Documents/julia_dcm/Manifest.toml`
  [bcd4f6db] DelayDiffEq v6.1.2
  [731186ca] RecursiveArrayTools v4.4.0
⌅ [0bca4576] SciMLBase v3.46.1
  [2efcf032] SymbolicIndexingInterface v0.3.54
  [961ee093] ModelingToolkit v11.39.0
  [7771a370] ModelingToolkitBase v1.65.0
  [1dea7af3] OrdinaryDiffEq v7.6.0
  [b1df2697] OrdinaryDiffEqTsit5 v2.1.3
  [91a5bcdd] Plots v1.41.6
  [0c5d862f] Symbolics v7.36.0
  [d1185830] SymbolicUtils v4.45.0

Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading.
  • Output of versioninfo()
Julia Version 1.12.6
Commit 15346901f00 (2026-04-09 19:20 UTC)
Build Info:
  Official https://julialang.org release
Platform Info:
  OS: macOS (arm64-apple-darwin24.0.0)
  CPU: 8 × Apple M2
  WORD_SIZE: 64
  LLVM: libLLVM-18.1.7 (ORCJIT, apple-m2)
  GC: Built with stock GC
Threads: 1 default, 1 interactive, 1 GC (on 4 virtual cores)

Additional context

The vector-time symbolic-index path in SciMLBase/src/solutions/ode_solutions.jl obtains getter = getsym(sol, idxs), creates interp_sol = augment(sol.interp(...), sol), and calls getter(interp_sol). For a non-Markovian observed variable, SymbolicIndexingInterface correctly requires get_history_function(interp_sol), but no accessor exists for the resulting DiffEqArray. In contrast, SciMLBase provides get_history_function(sol::AbstractODESolution).

Activity

  1. ChrisRackauckas-Claude commented on Sep 30, 2026

    @ChrisRackauckas-Claude
    Member

    Taking this — Cursor Agent CLI 2026.09.28-64d2043, model auto (local job 1527-cursor on amdci2.julia.csail.mit.edu).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions