In my opinion, DelayDiffEq is a lot more complicated than it should be, which also makes analysis and fixes more difficult. Hence I started to think about how the current situation could be improved.
Status quo
Currently, when initializing a new DDEIntegrator an ODEIntegrator is created (but not initialized) whose function, however, can never be evaluated since it has the wrong number of arguments. Hence in a quite confusing way it is wrapped, together with its solution, in a HistoryFunction, which itself is wrapped in a new differential equation of the form needed for OrdinaryDiffEq. Based on this, a DDEIntegrator is defined that duplicates all fields of an ODEIntegrator and uses this closure for function evaluations; hence OrdinaryDiffEq can be used for integration; in a quite advanced way the initial ODEIntegrator is updated to ensure a correct history function. Moreover, to reduce memory the caches of this ODEIntegrator are transferred to the DDEIntegrator, and the solution of the integrator is shortened already during integration if possible.
Ideas
- Create one fully functional
ODEIntegrator: This is currently not possible since the DDE function depends on argument h as well which itself depends on the interpolation of the ODEIntegrator that does not exist yet; possible solution:
- Wrap
h in a HistoryFunction that can be called as (::HistoryFunction)(integrator, t, ::Val{deriv}; idxs = nothing) (and so on):
- does not require the non-existing integrator at creation time
- requires the possibility to pass
integrator instead of parameters p during integration as it was discussed already when the breaking changes were introduced in January
- passing
integrator to the wrapper would still allow to only pass integrator.p to the wrapped initial function h
- Wrap
f and this HistoryFunction (probably together with analytic solution, gradient, Jacobian etc.) in a DDEFunction <: AbstractDiffEqFunction
- allows to define
(::DDEFunction)(du, u, integrator, t) (and so on), as required by OrdinaryDiffEq
- requires to pass
integrator instead of parameters p
- would still allow to pass only
integrator.p to f
- would group all different functions such as
f and h that belong together (e.g. usually the analytic solution depends on both)
- Wrap it inside a
DDEIntegrator which contains no duplicate fields:
- currently almost every update of
ODEIntegrator breaks DDEIntegrator
- Use
ODEIntegrator for integration and DDEIntegrator mostly for its control, i.e. for resets and fixed-point iteration:
- from previous experiences, the most difficult part would be to implement intra-/extrapolations and fixed-point iterations correctly
- Add possibility to only save dense output of certain indices:
- currently a very complicated algorithm exists to reduce the solution in special cases
- based on the implementation of RADAR5, one could (alternatively) save dense output only for certain indices (but still save solution at all or given time points for all or given indices)
- would be simpler probably, and would also work for state-dependent delays
In my opinion, DelayDiffEq is a lot more complicated than it should be, which also makes analysis and fixes more difficult. Hence I started to think about how the current situation could be improved.
Status quo
Currently, when initializing a new
DDEIntegratoranODEIntegratoris created (but not initialized) whose function, however, can never be evaluated since it has the wrong number of arguments. Hence in a quite confusing way it is wrapped, together with its solution, in aHistoryFunction, which itself is wrapped in a new differential equation of the form needed forOrdinaryDiffEq. Based on this, aDDEIntegratoris defined that duplicates all fields of anODEIntegratorand uses this closure for function evaluations; henceOrdinaryDiffEqcan be used for integration; in a quite advanced way the initialODEIntegratoris updated to ensure a correct history function. Moreover, to reduce memory the caches of thisODEIntegratorare transferred to theDDEIntegrator, and the solution of the integrator is shortened already during integration if possible.Ideas
ODEIntegrator: This is currently not possible since the DDE function depends on argumenthas well which itself depends on the interpolation of theODEIntegratorthat does not exist yet; possible solution:hin aHistoryFunctionthat can be called as(::HistoryFunction)(integrator, t, ::Val{deriv}; idxs = nothing)(and so on):integratorinstead of parameterspduring integration as it was discussed already when the breaking changes were introduced in Januaryintegratorto the wrapper would still allow to only passintegrator.pto the wrapped initial functionhfand thisHistoryFunction(probably together with analytic solution, gradient, Jacobian etc.) in aDDEFunction <: AbstractDiffEqFunction(::DDEFunction)(du, u, integrator, t)(and so on), as required byOrdinaryDiffEqintegratorinstead of parameterspintegrator.ptoffandhthat belong together (e.g. usually the analytic solution depends on both)DDEIntegratorwhich contains no duplicate fields:ODEIntegratorbreaksDDEIntegratorODEIntegratorfor integration andDDEIntegratormostly for its control, i.e. for resets and fixed-point iteration: