Interface Functions

Index provider interface

Mandatory methods

SymbolicIndexingInterface.variable_symbolsFunction
variable_symbols(indp, [i])

Return a vector of the symbolic variables being solved for in the index provider indp. If constant_structure(sys) == false this accepts an additional parameter indicating the current time index. The returned vector should not be mutated.

For types that implement Base.getindex with symbolic indices using this interface, the shorthand valp[solvedvariables] can be used as shorthand for valp[variable_symbols(sys)]. See: solvedvariables.

source
SymbolicIndexingInterface.default_valuesFunction
default_values(indp)

Return a dictionary mapping symbols in the index provider to their default value, if any. This includes parameter symbols. The dictionary must be mutable.

source
SymbolicIndexingInterface.all_variable_symbolsFunction
all_variable_symbols(indp)

Return a vector of variable symbols in the system, including observed quantities.

For types that implement Base.getindex with symbolic indices using this interface, The shorthand sys[allvariables] can be used as shorthand for valp[all_variable_symbols(indp)].

See: allvariables.

source

Optional Methods

SymbolicIndexingInterface.symbolic_containerFunction
symbolic_container(indp)

Using indp, return an object that implements the index provider interface. In case indp itself implements the interface, indp can be returned as-is. All index provider interface methods fall back to calling the same method on symbolic_container(indp), so this may be used for trivial implementations of the interface that forward all calls to another object.

Note that this method is optional. Thus the correct method to check for a fallback is:

hasmethod(symbolic_container, Tuple{typeof(indp)}) && symbolic_container(indp) != indp
source

Observed equation handling

SymbolicIndexingInterface.observedFunction
observed(indp, sym, [states])

Return the observed function of the given sym in indp. The returned function should have the signature (u, p) -> [values...] where u and p is the current state and parameter object, respectively. If istimedependent(indp) == true, the function should accept the current time t as its third parameter. If constant_structure(indp) == false, observed accepts a third parameter, which can either be a vector of symbols indicating the order of states or a time index, which identifies the order of states. This function does not need to be defined if is_observed always returns false. Thus, it is mandatory to always check is_observed before using this function.

If !is_markovian(indp), the returned function must have the signature (u, h, p, t) -> [values...] where h is the history function, which can be called to obtain past values of the state. The exact signature and semantics of h depend on how it is used inside the returned function. h is obtained from a value provider using get_history_function.

See also: is_time_dependent, is_markovian, constant_structure.

source
SymbolicIndexingInterface.parameter_observedFunction
parameter_observed(indp, sym)

Return the observed function of sym in indp. This functions similarly to observed except that u is not an argument of the returned function. For time- dependent systems, the returned function must have the signature (p, t) -> [values...]. For time-independent systems, the returned function must have the signature (p) -> [values...].

By default, this function returns nothing, indicating that the index provider does not support generating parameter observed functions.

source

Historical index providers

SymbolicIndexingInterface.is_markovianFunction
is_markovian(indp)

Check if an index provider represents a Markovian system. Markovian systems do not require knowledge of past states to simulate. This function is only applicable to index providers for which is_time_dependent(indp) returns true.

Non-Markovian index providers return observed functions with a different signature. All value providers associated with a non-markovian index provider must implement get_history_function.

Returns true by default.

source

Parameter timeseries

If the index provider contains parameters that change during the course of the simulation at discrete time points, it must implement the following methods to ensure correct functioning of getsym and getp for value providers that save the parameter timeseries. Note that there can be multiple parameter timeseries, in case different parameters may change at different times.

SymbolicIndexingInterface.ParameterTimeseriesIndexType
struct ParameterTimeseriesIndex
function ParameterTimeseriesIndex(timeseries_idx, parameter_idx)

A struct storing the index of the timeseries of a timeseries parameter in a parameter timeseries object. timeseries_idx refers to an index that identifies the timeseries that the parameter belongs to. parameter_idx refers to the index of the parameter's timeseries in that timeseries object. Note that parameter_idx may be different from the object returned by parameter_index for a given parameter. The two fields in this struct are timeseries_idx and parameter_idx.

source
SymbolicIndexingInterface.get_all_timeseries_indexesFunction
get_all_timeseries_indexes(indp, sym)

Return a Set of all unique timeseries indexes of variables in symbolic variable sym. sym may be a symbolic variable or expression, an array of symbolics, an index, or an array of indices. Continuous variables correspond to the ContinuousTimeseries timeseries index. Non-timeseries parameters do not have a timeseries index. Timeseries parameters have the same timeseries index as that returned by timeseries_parameter_index. Note that the independent variable corresponds to the ContinuousTimeseries timeseries index.

Any ambiguities should be resolved in favor of variables. For example, if 1 could refer to the variable at index 1 or parameter at index 1, it should be interpreted as the variable.

By default, this function returns Set([ContinuousTimeseries()]).

source

Value provider interface

State indexing

SymbolicIndexingInterface.NotTimeseriesType
struct NotTimeseries <: IsTimeseriesTrait end

Trait indicating a type does not contain timeseries data. This affects the behaviour of functions such as state_values and current_time. Note that if a type is NotTimeseries this only implies that it does not store timeseries data. It may still be time-dependent. For example, an ODEProblem only stores the initial state of a system, so it is NotTimeseries, but still time-dependent. This is the default trait variant for all types.

See also: Timeseries, is_timeseries.

source
SymbolicIndexingInterface.state_valuesFunction
state_values(valp)
state_values(valp, i)

Return an indexable collection containing the values of all states in the value provider p. If is_timeseries(valp) is Timeseries, return a vector of arrays, each of which contain the state values at the corresponding timestep. In this case, the two-argument version of the function can also be implemented to efficiently return the state values at timestep i. By default, the two-argument method calls state_values(valp)[i]. If i consists of multiple indices (for example, Colon, AbstractArray{Int}, AbstractArray{Bool}) specialized methods may be defined for efficiency. By default, state_values(valp, ::Colon) = state_values(valp) to avoid copying the timeseries.

If this function is called with an AbstractArray, it will return the same array.

See: is_timeseries

source
SymbolicIndexingInterface.set_state!Function
set_state!(valp, val, idx)

Set the state at index idx to val for value provider valp. This defaults to modifying state_values(valp). If any additional bookkeeping needs to be performed or the default implementation does not work for a particular type, this method needs to be defined to enable the proper functioning of setsym.

See: state_values

source
SymbolicIndexingInterface.current_timeFunction
current_time(valp)
current_time(valp, i)

Return the current time in the value provider valp. If is_timeseries(valp) is Timeseries, return the vector of timesteps at which the state value is saved. In this case, the two-argument version of the function can also be implemented to efficiently return the time at timestep i. By default, the two- argument method calls current_time(p)[i]. It is assumed that the timeseries is sorted in increasing order.

If i consists of multiple indices (for example, Colon, AbstractArray{Int}, AbstractArray{Bool}) specialized methods may be defined for efficiency. By default, current_time(valp, ::Colon) = current_time(valp) to avoid copying the timeseries.

By default, the single-argument version acts as the identity function if valp isa AbstractVector.

See: is_timeseries

source
SymbolicIndexingInterface.getsymFunction
getsym(indp, sym)

Return a function that takes a value provider and returns the value of the symbolic variable sym. If sym is not an observed quantity, the returned function can also directly be called with an array of values representing the state vector. sym can be an index into the state vector, a symbolic variable, a symbolic expression involving symbolic variables in the index provider indp, a parameter symbol, the independent variable symbol, or an array/tuple of the aforementioned. If the returned function is called with a timeseries object, it can also be given a second argument representing the index at which to return the value of sym.

At minimum, this requires that the value provider implement state_values. To support symbolic expressions, the value provider must implement observed, parameter_values and current_time.

This function typically does not need to be implemented, and has a default implementation relying on the above functions.

If the value provider is a parameter timeseries object, the same rules apply as getp. The difference here is that sym may also contain non-parameter symbols, and the values are always returned corresponding to the state timeseries.

source
getsym(bi::BatchedInterface)

Given a BatchedInterface composed from n index providers (and corresponding symbols), return a function which takes n corresponding value providers and returns an array of the values of the symbols in the union. The returned function can also be passed an AbstractArray of the appropriate eltype and size as its first argument, in which case the operation will populate the array in-place with the values of the symbols in the union.

Note that all of the value providers passed to the function returned by getsym must satisfy is_timeseries(prob) === NotTimeseries().

The value of the ith symbol in the union (obtained through variable_symbols(bi)[i]) is obtained from the problem corresponding to the associated index provider (i.e. the value provider at index associated_systems(bi)[i]).

See also: variable_symbols, associated_systems, is_timeseries, NotTimeseries.

source
SymbolicIndexingInterface.setsymFunction
setsym(sys, sym)

Return a function that takes a value provider and a value, and sets the the state sym to that value. Note that sym can be an index, a symbolic variable, or an array/tuple of the aforementioned.

Requires that the value provider implement state_values and the returned collection be a mutable reference to the state vector in the value provider. Alternatively, if this is not possible or additional actions need to be performed when updating state, set_state! can be defined. This function does not work on types for which is_timeseries is Timeseries.

source
setsym(bi::BatchedInterface)

Given a BatchedInterface composed from n index providers (and corresponding symbols), return a function which takes n corresponding problems and an array of the values, and updates each of the problems with the values of the corresponding symbols.

Note that all of the value providers passed to the function returned by setsym must satisfy is_timeseries(prob) === NotTimeseries().

Note that if any subset of the n index providers share common symbols (among those passed to BatchedInterface) then all of the corresponding value providers in the subset will be updated with the values of the common symbols.

See also: is_timeseries, NotTimeseries.

source
Note

getu and setu have been renamed to getsym and setsym respectively.

Historical value providers

Parameter indexing

SymbolicIndexingInterface.parameter_valuesFunction
parameter_values(valp)
parameter_values(valp, i)

Return an indexable collection containing the value of each parameter in valp. The two- argument version of this function returns the parameter value at index i. The two-argument version of this function will default to returning parameter_values(valp)[i].

If this function is called with an AbstractArray or Tuple, it will return the same array/tuple.

source
SymbolicIndexingInterface.set_parameter!Function
set_parameter!(valp, val, idx)

Set the parameter at index idx to val for value provider valp. This defaults to modifying parameter_values(valp). If any additional bookkeeping needs to be performed or the default implementation does not work for a particular type, this method needs to be defined to enable the proper functioning of setp.

See: parameter_values

source
SymbolicIndexingInterface.getpFunction
getp(indp, sym)

Return a function that takes an value provider, and returns the value of the parameter sym. The value provider has to at least store the values of parameters in the corresponding index provider. Note that sym can be an index, symbolic variable, or an array/tuple of the aforementioned.

If sym is an array/tuple of parameters, then the returned function can also be used as an in-place getter function. The first argument is the buffer (must be an AbstractArray) to which the parameter values should be written, and the second argument is the value provider.

Requires that the value provider implement parameter_values. This function may not always need to be implemented, and has a default implementation for collections that implement getindex.

If the returned function is used on a timeseries object which saves parameter timeseries, it can be used to index said timeseries. The timeseries object must implement is_parameter_timeseries and get_parameter_timeseries_collection. Additionally, the parameter object must implement with_updated_parameter_timeseries_values.

If sym is a timeseries parameter, the function will return the timeseries of the parameter if the value provider is a parameter timeseries object. An additional argument can be provided to the function indicating the specific indexes in the timeseries at which to access the values. If sym is an array of parameters, the following cases apply:

  • All parameters are non-timeseries parameters: The function returns the value of each parameter.
  • All parameters are timeseries parameters: All the parameters must belong to the same timeseries (otherwise getp will error). The function returns the timeseries of all parameter values, and can be accessed at specific indices in the timeseries.
  • A mix of timeseries and non-timeseries parameters: The function can only be used on non-timeseries objects and will return the value of each parameter at in the object.
source
SymbolicIndexingInterface.setpFunction
setp(indp, sym)

Return a function that takes a value provider and a value, and sets the parameter sym to that value. Note that sym can be an index, a symbolic variable, or an array/tuple of the aforementioned.

Requires that the value provider implement parameter_values and the returned collection be a mutable reference to the parameter object. In case parameter_values cannot return such a mutable reference, or additional actions need to be performed when updating parameters, set_parameter! must be implemented.

source
SymbolicIndexingInterface.setp_oopFunction
setp_oop(indp, sym)

Return a function which takes a value provider valp and a value val, and returns parameter_values(valp) with the parameters at sym set to val. This allows changing the types of values stored, and leverages remake_buffer. Note that sym can be an index, a symbolic variable, or an array/tuple of the aforementioned.

Requires that the value provider implement parameter_values and remake_buffer.

source
SymbolicIndexingInterface.ParameterIndexingProxyType
struct ParameterIndexingProxy

This struct wraps any struct implementing the value provider and index provider interfaces. It allows getindex and setindex! operations to get/set parameter values. Requires that the wrapped type support getp and setp for getting and setting parameter values respectively.

source
SymbolicIndexingInterface.show_paramsFunction
show_params(io::IO, pip::ParameterIndexingProxy; num_rows = 20, show_all = false, scalarize = true, kwargs...)

Method for customizing the table output. Keyword args:

  • num_rows
  • showall: whether to show all parameters. Overrides `numrows`.
  • scalarize: whether to scalarize array symbolics in the table output.
  • kwargs... are passed to the pretty_table call.
source

Parameter timeseries

If a solution object saves a timeseries of parameter values that are updated during the simulation (such as by callbacks), it must implement the following methods to ensure correct functioning of getsym and getp.

Parameter timeseries support requires that the value provider store the different timeseries in a ParameterTimeseriesCollection.

SymbolicIndexingInterface.ParameterTimeseriesCollectionType
struct ParameterTimeseriesCollection{T}
function ParameterTimeseriesCollection(collection)

A utility struct that helps in storing multiple parameter timeseries. It expects a collection of timseries objects (is_timeseries returns Timeseries) for each. Each of the timeseries objects should implement state_values and current_time. Effectively, the "states" of each contained timeseries object are the parameter values it stores the timeseries of.

The collection is expected to implement Base.eachindex, Base.iterate and Base.getindex. The indexes of the collection should agree with the timeseries indexes returned by calling timeseries_parameter_index on the corresponding index provider.

This type forwards eachindex, iterate and length to the contained collection. It implements Base.parent to allow access to the contained collection, and has the following getindex methods:

  • getindex(ptc::ParameterTimeseriesCollection, idx) = ptc.collection[idx].
  • getindex(::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex) returns the timeseries of the parameter referred to by idx.
  • getindex(::ParameterTimeseriesCollection, idx::ParameterTimeseriesIndex, subidx) returns the value of the parameter referred to by idx at the time index subidx.
  • Apart from these cases, if multiple indexes are provided the first is treated as a timeseries index, the second the time index in the timeseries, and the (optional) third the index of the parameter in an element of the timeseries.

The three-argument version of parameter_values is implemented for this type. The single-argument version of parameter_values returns the cached parameter object. This type does not implement any traits.

source
SymbolicIndexingInterface.with_updated_parameter_timeseries_valuesFunction
with_updated_parameter_timeseries_values(indp, params, args::Pair...)

Return an indexable collection containing the value of all parameters in params, with parameters belonging to specific timeseries updated to different values. Each element in args... contains the timeseries index as the first value, and the saved parameter values in that partition. Not all parameter timeseries have to be updated using this method. If an in-place update can be performed, it should be done and the modified params returned. This method falls back on the basis of symbolic_container(indp).

Note that here params is the parameter object.

source

Batched Queries and Updates

SymbolicIndexingInterface.BatchedInterfaceType
struct BatchedInterface{S <: AbstractVector, I}
function BatchedInterface(indp_syms::Tuple...)

A struct which stores information for batched calls to getsym or setsym. Given Tuples, where the first element of each tuple is an index provider and the second an array of symbolic variables (either states or parameters) in the index provider, BatchedInterface will compute the union of all symbols and associate each symbol with the first index provider with which it occurs.

For example, given two index providers s1 = SymbolCache([:x, :y, :z]) and s2 = SymbolCache([:y, :z, :w]), BatchedInterface((s1, [:x, :y]), (s2, [:y, :z])) will associate :x and :y with s1 and :z with s2. The information that s1 had associated symbols :x and :y and s2 had associated symbols :y and :z will also be retained internally.

BatchedInterface implements variable_symbols, is_variable, variable_index to query the order of symbols in the union.

See getsym and setsym for further details.

See also: associated_systems.

source
SymbolicIndexingInterface.associated_systemsFunction
associated_systems(bi::BatchedInterface)

Return an array of integers of the same length as variable_symbols(bi) where each value is the index of the index provider associated with the corresponding symbol in variable_symbols(bi).

source
SymbolicIndexingInterface.setsym_oopFunction
setsym_oop(indp, sym)

Return a function which takes a value provider valp and a value val, and returns state_values(valp), parameter_values(valp) with the states/parameters in sym set to the corresponding values in val. This allows changing the types of values stored, and leverages remake_buffer. Note that sym can be an index, a symbolic variable, or an array/tuple of the aforementioned. All entries s in sym must satisfy is_variable(indp, s) or is_parameter(indp, s).

Requires that the value provider implement state_values, parameter_values and remake_buffer.

source
setsym_oop(bi::BatchedInterface)

Given a BatchedInterface composed from n index providers (and corresponding symbols), return a function which takes n corresponding value providers and an array of values, and returns an n-tuple where each element is a 2-tuple consisting of the updated state values and parameter values of the corresponding value provider. Requires that the value provider implement state_values, parameter_values. The updates are performed out-of-place using remake_buffer.

Note that all of the value providers passed to the returned function must satisfy is_timeseries(prob) === NotTimeseries().

Note that if any subset of the n index providers share common symbols (among those passed to BatchedInterface) then all of the corresponding value providers in the subset will be updated with the values of the common symbols.

See also: is_timeseries, NotTimeseries.

source

Container objects

SymbolicIndexingInterface.remake_bufferFunction
remake_buffer(indp, oldbuffer, idxs, vals)

Return a copy of the buffer oldbuffer with at (optionally symbolic) indexes idxs replaced by corresponding values from vals. Both idxs and vals must be iterables of the same length. idxs may contain symbolic variables whose index in the buffer is determined using indp. The types of values in vals may not match the types of values stored at the corresponding indexes in the buffer, in which case the type of the buffer should be promoted accordingly. In general, this method should attempt to preserve the types of values stored in vals as much as possible. Types can be promoted for type-stability, to maintain performance. The returned buffer should be of the same type (ignoring type-parameters) as oldbuffer.

This method is already implemented for oldbuffer::AbstractArray and oldbuffer::Tuple, and supports static arrays as well.

The deprecated version of this method which takes a Dict mapping symbols to values instead of idxs and vals will dispatch to the new method. In addition if no remake_buffer method exists with the new signature, it will call remake_buffer(sys, oldbuffer, Dict(idxs .=> vals)).

Note that the new method signature allows idxs to be indexes, instead of requiring that they be symbolic variables. Thus, any type which implements the new method must also support indexes in idxs.

source

Symbolic Trait

SymbolicIndexingInterface.ArraySymbolicType
struct ArraySymbolic <: SymbolicTypeTrait end

Trait indicating type is a symbolic array. Calling collect on a symbolic array must return an AbstractArray containing ScalarSymbolic variables for each element in the array, in the same shape as the represented array. For example, if a is a symbolic array representing a 2x2 matrix, collect(a) must return a 2x2 array of scalar symbolic variables.

See also: ScalarSymbolic, NotSymbolic, symbolic_type

source
SymbolicIndexingInterface.hasnameFunction
hasname(x)

Check whether the given symbolic variable (for which symbolic_type(x) != NotSymbolic()) has a valid name as per getname. Defaults to true for x::Symbol.

source
SymbolicIndexingInterface.symbolic_evaluateFunction
symbolic_evaluate(expr, syms::Dict; kwargs...)

Return the value of symbolic expression expr where the values of variables involved are obtained from the dictionary syms. The keys of syms are symbolic variables (not expressions of variables). The values of syms can be values or symbolic expressions.

The returned value should either be a value or an expression involving symbolic variables not present as keys in syms.

The function can take additional keyword arguments to control implementation-specific behavior.

This is already implemented for symbolic_evaluate(expr::Union{Symbol, Expr}, syms::Dict).

source

Types

SymbolicIndexingInterface.SymbolCacheType
struct SymbolCache
function SymbolCache(vars, [params, [indepvars]]; defaults = Dict(), timeseries_parameters = nothing)

A struct implementing the index provider interface for a collection of variables, parameters, and independent variables. vars and params can be specified as arrays (in which case the index of a symbol is its index in the array) or AbstractDicts mapping symbols to indices. It is considered time dependent if it contains at least one independent variable.

It returns true for is_observed(::SymbolCache, sym) if sym isa Union{Expr, Array{Expr}, Tuple{Vararg{Expr}}. Functions can be generated using observed for Exprs involving variables in the SymbolCache if it has at most one independent variable.

defaults is an AbstractDict mapping variables and/or parameters to their default initial values. The default initial values can also be other variables/ parameters or expressions of them. timeseries_parameters is an AbstractDict the timeseries parameters in params to their ParameterTimeseriesIndex indexes.

The independent variable may be specified as a single symbolic variable instead of an array containing a single variable if the system has only one independent variable.

source
SymbolicIndexingInterface.ProblemStateType
struct ProblemState
function ProblemState(; u = nothing, p = nothing, t = nothing, h = nothing)

A value provider struct which can be used as an argument to the function returned by getsym or setsym. It stores the state vector, parameter object and current time, and forwards calls to state_values, parameter_values, current_time, set_state!, set_parameter! to the contained objects.

A history function may be provided using the h keyword, which will be returned with get_history_function.

source