Symbolic save_idxs and Saved Subsystems

When a symbolic problem is solved with save_idxs, a solution may contain only a subset of the original state variables or time-series parameters. The solution interface still needs symbolic indexing to behave as if the original system were available.

The saved-subsystem interface records how the saved arrays map back to the original symbolic system. It is used by solution indexing, state_values, and time-series parameter storage so solver packages can save less data without breaking symbolic queries.

Required Behavior

  • sol[x] should use the saved value for a saved symbolic state x.
  • state_values(sol, i) should reconstruct a full state-like object by starting from the problem's original state layout and replacing the saved entries.
  • Time-series parameters saved from discrete callbacks should be recognized as time-series parameters only when that parameter's time series was saved.
  • Saving only time-series parameters is valid; the state save_idxs passed to a low-level solver is then Int[].
  • Observed variables are not supported in save_idxs. Selecting an observed quantity raises an ArgumentError that names the limitation and lists workarounds (full-state solve + sol[obs], DiffEqCallbacks.SavingCallback, or saving the dependent states). Supporting observed save_idxs requires evaluating observed functions at save points and extending SavedSubsystem with an observed-column map; see SciML/DifferentialEquations.jl#1036.

Solver-Author Flow

Solver packages that construct solutions from symbolic save_idxs should call get_save_idxs_and_saved_subsystem(prob, save_idxs) before solving. The first return value is the integer state selection passed to the solver. The second return value is passed to build_solution(...; saved_subsystem = ss) and then stored on the concrete solution or exposed through get_saved_subsystem.

Concrete time-series solution types with a saved subsystem must route symbolic time-series parameter operations through SavedSubsystemWithFallback; the fallback implementations for AbstractTimeseriesSolution do this when get_saved_subsystem(sol) !== nothing.

API

SciMLBase.get_saved_subsystemFunction
get_saved_subsystem(sol) -> Union{SavedSubsystem, Nothing}

Return the saved-subsystem metadata carried by a solution with symbolic or partial save_idxs.

Concrete time-series solution types that store only a subset of symbolic states or time-series parameters should provide a saved_subsystem field or overload this function to return the corresponding SavedSubsystem. Return nothing when the solution saved the full symbolic state, when the problem has no symbolic index provider, or when no symbolic subset metadata is required.

Solution indexing code uses this hook to decide whether symbolic queries should be forwarded directly to symbolic_container(sol) or through SavedSubsystemWithFallback.

source
SciMLBase.SavedSubsystemType

A representation of the symbolic subsystem saved in a solution.

SavedSubsystem(indp, pobj, saved_idxs) records how the saved values relate to the original symbolic system described by the index provider indp and parameter object pobj. saved_idxs may contain integer state indexes, symbolic state variables, symbolic arrays of state variables, or symbolic time-series parameters. Every symbolic entry must resolve to either a state variable or a time-series parameter of indp; other symbolic entries are rejected. Observed variables (quantities computed from the full state via observed, not stored in u) are not supported in save_idxs yet and raise a dedicated ArgumentError pointing at workarounds and DifferentialEquations.jl#1036.

The object is stored on solution types when save_idxs omits part of the symbolic state or time-series parameter set. It lets sol[x], state_values, and time-series parameter queries keep using the original symbols even though the solution arrays contain only the saved subset.

The constructor returns nothing when no metadata is needed, including saved_idxs === nothing, unavailable symbolic metadata, or requests that save all state variables and all time-series parameters.

Solver-author contract:

  • Pass the returned object to build_solution(...; saved_subsystem = ss) when constructing a solution from symbolic save_idxs.
  • Store the object on concrete time-series solution types as saved_subsystem, or overload get_saved_subsystem.
  • Forward symbolic time-series parameter operations through SavedSubsystemWithFallback when saved_subsystem !== nothing.
source
SciMLBase.get_saved_state_idxsFunction
get_saved_state_idxs(ss::SciMLBase.SavedSubsystem) -> Any

Return the original-system state indexes saved by a SavedSubsystem.

The returned vector is ordered like the saved state portion of the solution. It does not include saved time-series parameters; when save_idxs selected only time-series parameters, this returns an empty vector. Solver setup uses this helper to translate symbolic save_idxs into the integer state indexes passed to low-level save machinery.

source
SciMLBase.SavedSubsystemWithFallbackType
struct SavedSubsystemWithFallback{S<:SciMLBase.SavedSubsystem, T}

A symbolic indexing adapter for subsetted solutions.

SavedSubsystemWithFallback(saved_subsystem, fallback) combines the subset map with the original symbolic index provider. The fallback is returned from symbolic_container, while time-series parameter queries are filtered and renumbered through saved_subsystem.

Use this wrapper whenever a solution saved only part of the symbolic state or time-series parameter set. It preserves the original symbolic names while ensuring:

  • unsaved time-series parameters are reported as unavailable in the saved solution,
  • fully saved time-series partitions reuse the fallback representation, and
  • partially saved partitions allocate compact saved buffers and map updates back to the original parameter object.

The wrapper implements the time-series parameter hooks used by the SymbolicIndexingInterface: is_timeseries_parameter, timeseries_parameter_index, create_parameter_timeseries_collection, get_saveable_values, and with_updated_parameter_timeseries_values.

source
SciMLBase.get_save_idxs_and_saved_subsystemFunction
get_save_idxs_and_saved_subsystem(
    prob,
    _::Nothing
) -> Tuple{Nothing, Nothing}

Translate user-facing save_idxs into solver state indexes and subsystem metadata.

Given a SciML problem prob and a possibly symbolic save_idxs, return (state_save_idxs, saved_subsystem). state_save_idxs is the integer-only state selection that should be passed to solver save machinery, while saved_subsystem is the SavedSubsystem to pass to build_solution.

The helper preserves the scalar/vector shape of the user's request where it matters: scalar state selections remain scalar, vector state selections remain vectors, and selections containing only time-series parameters return Int[] for the state portion. Either return value may be nothing when no subset handling is needed.

source
SciMLBase.create_parameter_timeseries_collectionFunction
create_parameter_timeseries_collection(sys, ps, tspan)

Create the discrete parameter time-series storage for a solution.

sys is a symbolic index provider, ps is the parameter object used by the problem or integrator, and tspan is the solve time span. Return a SymbolicIndexingInterface.ParameterTimeseriesCollection when the system has time-series parameters that should be saved alongside the continuous state. Return nothing when there are no such parameters.

Implementations should allocate one time-series buffer per discrete parameter partition and make parameter_values(collection) return the parameter object that should be updated during interpolation and symbolic indexing. The fallback delegates to symbolic_container(sys) when available and otherwise returns nothing.

source
SciMLBase.get_saveable_valuesFunction
get_saveable_values(sys, ps, timeseries_idx)

Return the values that should be appended to a discrete parameter time series.

timeseries_idx selects the time-series partition to save from parameter object ps. The returned value must match the element type and shape of the buffer created by create_parameter_timeseries_collection for that partition. Return nothing when no value should be saved for timeseries_idx; in that case save_discretes! leaves the corresponding time series unchanged.

source
SciMLBase.save_discretes!Function
save_discretes!(integ::DEIntegrator, timeseries_idx)

Save one discrete parameter time-series partition at the integrator's current time.

This obtains the current parameter object from integ, calls get_saveable_values, and appends the returned values to the solution's discrete storage for timeseries_idx. If get_saveable_values returns nothing, no value is saved. When skip_duplicates = true, an implementation may avoid appending a second value at the same saved time.

source
save_discretes!(
    integrator::SciMLBase.DEIntegrator,
    cb::Union{ContinuousCallback, DiscreteCallback};
    skip_duplicates
)

Save the discrete parameter partitions associated with callback cb.

Callbacks created by ModelingToolkit may carry saved_clock_partitions metadata. When a callback triggers, this helper appends the corresponding time-series parameter values from integrator to the solution's discrete storage. For VectorContinuousCallback, the three-argument method saves only the partitions associated with event index i; the two-argument method saves all configured vector-event partitions.

Keyword arguments

  • skip_duplicates: Skip saving variables that have already been saved at the current time.
source