diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 40638ad..bcc1a43 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.0","generation_timestamp":"2024-01-04T13:45:45","documenter_version":"1.2.1"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.0","generation_timestamp":"2024-01-04T15:33:17","documenter_version":"1.2.1"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index 78f8f74..bac2d9b 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -3,7 +3,7 @@ function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-90474609-3', {'page_path': location.pathname + location.search + location.hash}); -

Interface Functions

Mandatory methods

SymbolicIndexingInterface.symbolic_containerFunction
symbolic_container(p)

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

source
SymbolicIndexingInterface.variable_symbolsFunction
variable_symbols(sys, [i])

Return a vector of the symbolic variables being solved for in the system sys. 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 sys[solvedvariables] can be used as shorthand for sys[variable_symbols(sys)]. See: solvedvariables.

source
SymbolicIndexingInterface.all_variable_symbolsFunction
all_variable_symbols(sys)

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 sys[all_variable_symbols(sys)]. See: allvariables.

source

Optional Methods

Observed equation handling

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

Return the observed function of the given sym in sys. The returned function should have the signature (u, p) -> [values...] where u and p is the current state and parameter vector, respectively. If istimedependent(sys) == true, the function should accept the current time t as its third parameter. If constant_structure(sys) == false, accept 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.

See also: is_time_dependent, constant_structure

source

Parameter indexing

SymbolicIndexingInterface.set_parameter!Function
set_parameter!(sys, val, idx)

Set the parameter at index idx to val for system sys. This defaults to modifying parameter_values(sys). 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(sys, p)

Return a function that takes an integrator or solution of sys, and returns the value of the parameter p. Note that p can be a direct numerical index or a symbolic value. Requires that the integrator or solution implement parameter_values. This function typically does not need to be implemented, and has a default implementation relying on parameter_values.

source
SymbolicIndexingInterface.setpFunction
setp(sys, p)

Return a function that takes an integrator of sys and a value, and sets the parameter p to that value. Note that p can be a direct numerical index or a symbolic value. Requires that the integrator implement parameter_values and the returned collection be a mutable reference to the parameter vector in the integrator. 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

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.set_state!Function
set_state!(sys, val, idx)

Set the state at index idx to val for system sys. This defaults to modifying state_values(sys). 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 setu.

See: state_values

source
SymbolicIndexingInterface.getuFunction
getu(sys, sym)

Return a function that takes an integrator, problem or solution of sys, and returns the value of the symbolic sym. sym can be a direct index into the state vector, a symbolic state, a symbolic expression involving symbolic quantities in the system sys, or an array/tuple of the aforementioned.

At minimum, this requires that the integrator, problem or solution implement state_values. To support symbolic expressions, the integrator or problem 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.

source
SymbolicIndexingInterface.setuFunction
setu(sys, sym)

Return a function that takes an integrator or problem of sys and a value, and sets the the state sym to that value. Note that sym can be a direct numerical index, a symbolic state, or an array/tuple of the aforementioned.

Requires that the integrator implement state_values and the returned collection be a mutable reference to the state vector in the integrator/problem. 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

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

Types

SymbolicIndexingInterface.SymbolCacheType
struct SymbolCache{V,P,I}
-function SymbolCache(vars, [params, [indepvars]])

A struct implementing the symbolic indexing interface for the trivial case of having a vector of variables, parameters, and independent variables. This struct does not implement observed, and is_observed returns false for all input symbols. It is considered time dependent if it contains at least one independent variable.

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
+

Interface Functions

Mandatory methods

SymbolicIndexingInterface.symbolic_containerFunction
symbolic_container(p)

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

source
SymbolicIndexingInterface.variable_symbolsFunction
variable_symbols(sys, [i])

Return a vector of the symbolic variables being solved for in the system sys. 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 sys[solvedvariables] can be used as shorthand for sys[variable_symbols(sys)]. See: solvedvariables.

source
SymbolicIndexingInterface.all_variable_symbolsFunction
all_variable_symbols(sys)

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 sys[all_variable_symbols(sys)]. See: allvariables.

source

Optional Methods

Observed equation handling

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

Return the observed function of the given sym in sys. The returned function should have the signature (u, p) -> [values...] where u and p is the current state and parameter vector, respectively. If istimedependent(sys) == true, the function should accept the current time t as its third parameter. If constant_structure(sys) == false, accept 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.

See also: is_time_dependent, constant_structure

source

Parameter indexing

SymbolicIndexingInterface.set_parameter!Function
set_parameter!(sys, val, idx)

Set the parameter at index idx to val for system sys. This defaults to modifying parameter_values(sys). 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(sys, p)

Return a function that takes an integrator or solution of sys, and returns the value of the parameter p. Note that p can be a direct numerical index or a symbolic value. Requires that the integrator or solution implement parameter_values. This function typically does not need to be implemented, and has a default implementation relying on parameter_values.

source
SymbolicIndexingInterface.setpFunction
setp(sys, p)

Return a function that takes an integrator of sys and a value, and sets the parameter p to that value. Note that p can be a direct numerical index or a symbolic value. Requires that the integrator implement parameter_values and the returned collection be a mutable reference to the parameter vector in the integrator. 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.ParameterIndexingProxyType
struct ParameterIndexingProxy

This struct wraps any struct implementing the symbolic indexing interface. 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

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.set_state!Function
set_state!(sys, val, idx)

Set the state at index idx to val for system sys. This defaults to modifying state_values(sys). 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 setu.

See: state_values

source
SymbolicIndexingInterface.getuFunction
getu(sys, sym)

Return a function that takes an integrator, problem or solution of sys, and returns the value of the symbolic sym. sym can be a direct index into the state vector, a symbolic state, a symbolic expression involving symbolic quantities in the system sys, or an array/tuple of the aforementioned.

At minimum, this requires that the integrator, problem or solution implement state_values. To support symbolic expressions, the integrator or problem 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.

source
SymbolicIndexingInterface.setuFunction
setu(sys, sym)

Return a function that takes an integrator or problem of sys and a value, and sets the the state sym to that value. Note that sym can be a direct numerical index, a symbolic state, or an array/tuple of the aforementioned.

Requires that the integrator implement state_values and the returned collection be a mutable reference to the state vector in the integrator/problem. 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

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

Types

SymbolicIndexingInterface.SymbolCacheType
struct SymbolCache{V,P,I}
+function SymbolCache(vars, [params, [indepvars]])

A struct implementing the symbolic indexing interface for the trivial case of having a vector of variables, parameters, and independent variables. This struct does not implement observed, and is_observed returns false for all input symbols. It is considered time dependent if it contains at least one independent variable.

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
diff --git a/dev/assets/Manifest.toml b/dev/assets/Manifest.toml index 17acc8b..e86dd97 100644 --- a/dev/assets/Manifest.toml +++ b/dev/assets/Manifest.toml @@ -1416,10 +1416,10 @@ uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" version = "0.6.12" [[deps.RecursiveArrayTools]] -deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "Requires", "SparseArrays", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] -git-tree-sha1 = "27ee1c03e732c488ecce1a25f0d7da9b5d936574" +deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "IteratorInterfaceExtensions", "LinearAlgebra", "RecipesBase", "SparseArrays", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface", "Tables"] +git-tree-sha1 = "264026749612181b79275685eede9af07c0ee588" uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" -version = "3.3.3" +version = "3.4.0" [deps.RecursiveArrayTools.extensions] RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" diff --git a/dev/complete_sii/index.html b/dev/complete_sii/index.html index f4c07e4..2e8fac5 100644 --- a/dev/complete_sii/index.html +++ b/dev/complete_sii/index.html @@ -3,7 +3,7 @@ function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'UA-90474609-3', {'page_path': location.pathname + location.search + location.hash}); -

Implementing the Complete Symbolic Indexing Interface

This tutorial will show how to define the entire Symbolic Indexing Interface on an ExampleSystem:

struct ExampleSystem
+

Implementing the Complete Symbolic Indexing Interface

This tutorial will show how to define the entire Symbolic Indexing Interface on an ExampleSystem:

struct ExampleSystem
   state_index::Dict{Symbol,Int}
   parameter_index::Dict{Symbol,Int}
   independent_variable::Union{Symbol,Nothing}
@@ -150,7 +150,19 @@
 get_tuple(sol) # [(3.0, 18.0), (3.5, 24.5)]

Note that setu is not designed to work for Timeseries objects.

If a type needs to perform some additional actions when updating the state/parameters or if it is not possible to return a mutable reference to the state/parameter vector which can directly be modified, the functions set_state! and/or set_parameter! can be used. For example, suppose our ExampleIntegrator had an additional field u_modified::Bool to allow it to keep track of when a discontinuity occurs and handle it appropriately. This flag needs to be set to true whenever the state is modified. The set_state! function can then be implemented as follows:

function SymbolicIndexingInterface.set_state!(integrator::ExampleIntegrator, val, idx)
   integrator.u[idx] = val
   integrator.u_modified = true
-end

Implementing the SymbolicTypeTrait for a type

The SymbolicTypeTrait is used to identify values that can act as symbolic variables. It has three variants:

  • NotSymbolic for quantities that are not symbolic. This is the default for all types.
  • ScalarSymbolic for quantities that are symbolic, and represent a single logical value.
  • ArraySymbolic for quantities that are symbolic, and represent an array of values. Types implementing this trait must return an array of ScalarSymbolic variables of the appropriate size and dimensions when collected.

The trait is implemented through the symbolic_type function. Consider the following example types:

struct MySym
+end

The ParameterIndexingProxy

ParameterIndexingProxy is a wrapper around another type which implements the interface and allows using getp and setp to get and set parameter values. This allows for a cleaner interface for parameter indexing. Consider the following example for ExampleIntegrator:

function Base.getproperty(obj::ExampleIntegrator, sym::Symbol)
+  if sym === :ps
+    return ParameterIndexingProxy(obj)
+  else
+    return getfield(obj, sym)
+  end
+end

This enables the following API:

integrator = ExampleIntegrator([1.0, 2.0, 3.0], [4.0, 5.0], 6.0, Dict(:x => 1, :y => 2, :z => 3), Dict(:a => 1, :b => 2), :t)
+
+integrator.ps[:a] # 4.0
+getp(integrator, :a)(integrator) # functionally the same as above
+
+integrator.ps[:b] = 3.0
+setp(integrator, :b)(integrator, 3.0) # functionally the same as above

Implementing the SymbolicTypeTrait for a type

The SymbolicTypeTrait is used to identify values that can act as symbolic variables. It has three variants:

  • NotSymbolic for quantities that are not symbolic. This is the default for all types.
  • ScalarSymbolic for quantities that are symbolic, and represent a single logical value.
  • ArraySymbolic for quantities that are symbolic, and represent an array of values. Types implementing this trait must return an array of ScalarSymbolic variables of the appropriate size and dimensions when collected.

The trait is implemented through the symbolic_type function. Consider the following example types:

struct MySym
   name::Symbol
 end
 
@@ -169,4 +181,4 @@
     MySym(Symbol(sym.name, :_, join(idxs, "_")))
     for idxs in Iterators.product(Base.OneTo.(sym.size)...)
   ]
-end

hasname is not required to always be true for symbolic types. For example, Symbolics.Num returns false whenever the wrapped value is a number, or an expression.

+end

hasname is not required to always be true for symbolic types. For example, Symbolics.Num returns false whenever the wrapped value is a number, or an expression.

diff --git a/dev/index.html b/dev/index.html index c50df42..f96cd96 100644 --- a/dev/index.html +++ b/dev/index.html @@ -166,7 +166,7 @@ [e6cf234a] RandomNumbers v1.5.3 [3cdcf5f2] RecipesBase v1.3.4 [01d81517] RecipesPipeline v0.6.12 - [731186ca] RecursiveArrayTools v3.3.3 + [731186ca] RecursiveArrayTools v3.4.0 [f2c3362d] RecursiveFactorization v0.2.21 [189a3867] Reexport v1.2.2 [2792f1a3] RegistryInstances v0.1.0 @@ -351,4 +351,4 @@ [8e850b90] libblastrampoline_jll v5.8.0+1 [8e850ede] nghttp2_jll v1.52.0+1 [3f19e933] p7zip_jll v17.4.0+2 -Info Packages marked with ⌃ and ⌅ have new versions available. Those with ⌃ may be upgradable, but those with ⌅ are restricted by compatibility constraints from upgrading. To see why use `status --outdated -m`

You can also download the manifest file and the project file.

+Info Packages marked with ⌃ and ⌅ have new versions available. Those with ⌃ may be upgradable, but those with ⌅ are restricted by compatibility constraints from upgrading. To see why use `status --outdated -m`

You can also download the manifest file and the project file.

diff --git a/dev/search_index.js b/dev/search_index.js index a86388d..533dca9 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"complete_sii/#Implementing-the-Complete-Symbolic-Indexing-Interface","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"This tutorial will show how to define the entire Symbolic Indexing Interface on an ExampleSystem:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"struct ExampleSystem\n state_index::Dict{Symbol,Int}\n parameter_index::Dict{Symbol,Int}\n independent_variable::Union{Symbol,Nothing}\n # mapping from observed variable to Expr to calculate its value\n observed::Dict{Symbol,Expr}\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Not all the methods in the interface are required. Some only need to be implemented if a type supports specific functionality. Consider the following struct, which needs to implement the interface:","category":"page"},{"location":"complete_sii/#Mandatory-methods","page":"Implementing the Complete Symbolic Indexing Interface","title":"Mandatory methods","text":"","category":"section"},{"location":"complete_sii/#Simple-Indexing-Functions","page":"Implementing the Complete Symbolic Indexing Interface","title":"Simple Indexing Functions","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"These are the simple functions which describe how to turn symbols into indices.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"function SymbolicIndexingInterface.is_variable(sys::ExampleSystem, sym)\n haskey(sys.state_index, sym)\nend\n\nfunction SymbolicIndexingInterface.variable_index(sys::ExampleSystem, sym)\n get(sys.state_index, sym, nothing)\nend\n\nfunction SymbolicIndexingInterface.variable_symbols(sys::ExampleSystem)\n collect(keys(sys.state_index))\nend\n\nfunction SymbolicIndexingInterface.is_parameter(sys::ExampleSystem, sym)\n haskey(sys.parameter_index, sym)\nend\n\nfunction SymbolicIndexingInterface.parameter_index(sys::ExampleSystem, sym)\n get(sys.parameter_index, sym, nothing)\nend\n\nfunction SymbolicIndexingInterface.parameter_symbols(sys::ExampleSystem)\n collect(keys(sys.parameter_index))\nend\n\nfunction SymbolicIndexingInterface.is_independent_variable(sys::ExampleSystem, sym)\n # note we have to check separately for `nothing`, otherwise\n # `is_independent_variable(p, nothing)` would return `true`.\n sys.independent_variable !== nothing && sym === sys.independent_variable\nend\n\nfunction SymbolicIndexingInterface.independent_variable_symbols(sys::ExampleSystem)\n sys.independent_variable === nothing ? [] : [sys.independent_variable]\nend\n\nfunction SymbolicIndexingInterface.is_time_dependent(sys::ExampleSystem)\n sys.independent_variable !== nothing\nend\n\nSymbolicIndexingInterface.constant_structure(::ExampleSystem) = true\n\nfunction SymbolicIndexingInterface.all_solvable_symbols(sys::ExampleSystem)\n return vcat(\n collect(keys(sys.state_index)),\n collect(keys(sys.observed)),\n )\nend\n\nfunction SymbolicIndexingInterface.all_symbols(sys::ExampleSystem)\n return vcat(\n all_solvable_symbols(sys),\n collect(keys(sys.parameter_index)),\n sys.independent_variable === nothing ? Symbol[] : sys.independent_variable\n )\nend","category":"page"},{"location":"complete_sii/#Observed-Equation-Handling","page":"Implementing the Complete Symbolic Indexing Interface","title":"Observed Equation Handling","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"These are for handling symbolic expressions and generating equations which are not directly in the solution vector.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"using RuntimeGeneratedFunctions\nRuntimeGeneratedFunctions.init(@__MODULE__)\n\n# this type accepts `Expr` for observed expressions involving state/parameter/observed\n# variables\nSymbolicIndexingInterface.is_observed(sys::ExampleSystem, sym) = sym isa Expr || sym isa Symbol && haskey(sys.observed, sym)\n\nfunction SymbolicIndexingInterface.observed(sys::ExampleSystem, sym::Expr)\n # generate a function with the appropriate signature\n if is_time_dependent(sys)\n fn_expr = :(\n function gen(u, p, t)\n # assign a variable for each state symbol it's value in u\n $([:($var = u[$idx]) for (var, idx) in pairs(sys.state_index)]...)\n # assign a variable for each parameter symbol it's value in p\n $([:($var = p[$idx]) for (var, idx) in pairs(sys.parameter_index)]...)\n # assign a variable for the independent variable\n $(sys.independent_variable) = t\n # return the value of the expression\n return $sym\n end\n )\n else\n fn_expr = :(\n function gen(u, p)\n # assign a variable for each state symbol it's value in u\n $([:($var = u[$idx]) for (var, idx) in pairs(sys.state_index)]...)\n # assign a variable for each parameter symbol it's value in p\n $([:($var = p[$idx]) for (var, idx) in pairs(sys.parameter_index)]...)\n # return the value of the expression\n return $sym\n end\n )\n end\n return @RuntimeGeneratedFunction(fn_expr)\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"In case a type does not support such observed quantities, is_observed must be defined to always return false, and observed does not need to be implemented.","category":"page"},{"location":"complete_sii/#Note-about-constant-structure","page":"Implementing the Complete Symbolic Indexing Interface","title":"Note about constant structure","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Note that the method definitions are all assuming constant_structure(p) == true.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"In case constant_structure(p) == false, the following methods would change:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"constant_structure(::ExampleSystem) = false\nvariable_index(sys::ExampleSystem, sym) would become variable_index(sys::ExampleSystem, sym i) where i is the time index at which the index of sym is required.\nvariable_symbols(sys::ExampleSystem) would become variable_symbols(sys::ExampleSystem, i) where i is the time index at which the variable symbols are required.\nobserved(sys::ExampleSystem, sym) would become observed(sys::ExampleSystem, sym, i) where i is either the time index at which the index of sym is required or a Vector of state symbols at the current time index.","category":"page"},{"location":"complete_sii/#Optional-methods","page":"Implementing the Complete Symbolic Indexing Interface","title":"Optional methods","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Note that observed is optional if is_observed is always false, or the type is only responsible for identifying observed values and observed will always be called on a type that wraps this type. An example is ModelingToolkit.AbstractSystem, which can identify whether a value is observed, but cannot implement observed itself.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Other optional methods relate to indexing functions. If a type contains the values of parameter variables, it must implement parameter_values. This allows the default definitions of getp and setp to work. While setp is not typically useful for solution objects, it may be useful for integrators. Typically, the default implementations for getp and setp will suffice, and manually defining them is not necessary.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"function SymbolicIndexingInterface.parameter_values(sys::ExampleSystem)\n sys.p\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"If a type contains the value of state variables, it can define state_values to enable the usage of getu and setu. These methods retturn getter/ setter functions to access or update the value of a state variable (or a collection of them). If the type also supports generating observed functions, getu also enables returning functions to access the value of arbitrary expressions involving the system's symbols. This also requires that the type implement parameter_values and current_time (if the system is time-dependent).","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Consider the following ExampleIntegrator","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"mutable struct ExampleIntegrator\n u::Vector{Float64}\n p::Vector{Float64}\n t::Float64\n sys::ExampleSystem\nend\n\n# define a fallback for the interface methods\nSymbolicIndexingInterface.symbolic_container(integ::ExampleIntegrator) = integ.sys\nSymbolicIndexingInterface.state_values(sys::ExampleIntegrator) = sys.u\nSymbolicIndexingInterface.parameter_values(sys::ExampleIntegrator) = sys.p\nSymbolicIndexingInterface.current_time(sys::ExampleIntegrator) = sys.t","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Then the following example would work:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"sys = ExampleSystem(Dict(:x => 1, :y => 2, :z => 3), Dict(:a => 1, :b => 2), :t, Dict())\nintegrator = ExampleIntegrator([1.0, 2.0, 3.0], [4.0, 5.0], 6.0, sys)\ngetx = getu(sys, :x)\ngetx(integrator) # 1.0\n\nget_expr = getu(sys, :(x + y + t))\nget_expr(integrator) # 13.0\n\nsetx! = setu(sys, :y)\nsetx!(integrator, 0.0)\ngetx(integrator) # 0.0","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"In case a type stores timeseries data (such as solutions), then it must also implement the Timeseries trait. The type would then return a timeseries from state_values and current_time and the function returned from getu would then return a timeseries as well. For example, consider the ExampleSolution below:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"struct ExampleSolution\n u::Vector{Vector{Float64}}\n t::Vector{Float64}\n p::Vector{Float64}\n sys::ExampleSystem\nend\n\n# define a fallback for the interface methods\nSymbolicIndexingInterface.symbolic_container(sol::ExampleSolution) = sol.sys\nSymbolicIndexingInterface.parameter_values(sol::ExampleSolution) = sol.p\n# define the trait\nSymbolicIndexingInterface.is_timeseries(::Type{ExampleSolution}) = Timeseries()\n# both state_values and current_time return a timeseries, which must be\n# the same length\nSymbolicIndexingInterface.state_values(sol::ExampleSolution) = sol.u\nSymbolicIndexingInterface.current_time(sol::ExampleSolution) = sol.t","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Then the following example would work:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"# using the same system that the ExampleIntegrator used\nsol = ExampleSolution([[1.0, 2.0, 3.0], [1.5, 2.5, 3.5]], [4.0, 5.0], [6.0, 7.0], sys)\ngetx = getu(sys, :x)\ngetx(sol) # [1.0, 1.5]\n\nget_expr = getu(sys, :(x + y + t))\nget_expr(sol) # [9.0, 11.0]\n\nget_arr = getu(sys, [:y, :(x + a)])\nget_arr(sol) # [[2.0, 5.0], [2.5, 5.5]]\n\nget_tuple = getu(sys, (:z, :(z * t)))\nget_tuple(sol) # [(3.0, 18.0), (3.5, 24.5)]","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Note that setu is not designed to work for Timeseries objects.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"If a type needs to perform some additional actions when updating the state/parameters or if it is not possible to return a mutable reference to the state/parameter vector which can directly be modified, the functions set_state! and/or set_parameter! can be used. For example, suppose our ExampleIntegrator had an additional field u_modified::Bool to allow it to keep track of when a discontinuity occurs and handle it appropriately. This flag needs to be set to true whenever the state is modified. The set_state! function can then be implemented as follows:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"function SymbolicIndexingInterface.set_state!(integrator::ExampleIntegrator, val, idx)\n integrator.u[idx] = val\n integrator.u_modified = true\nend","category":"page"},{"location":"complete_sii/#Implementing-the-SymbolicTypeTrait-for-a-type","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the SymbolicTypeTrait for a type","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"The SymbolicTypeTrait is used to identify values that can act as symbolic variables. It has three variants:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"NotSymbolic for quantities that are not symbolic. This is the default for all types.\nScalarSymbolic for quantities that are symbolic, and represent a single logical value.\nArraySymbolic for quantities that are symbolic, and represent an array of values. Types implementing this trait must return an array of ScalarSymbolic variables of the appropriate size and dimensions when collected.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"The trait is implemented through the symbolic_type function. Consider the following example types:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"struct MySym\n name::Symbol\nend\n\nstruct MySymArr{N}\n name::Symbol\n size::NTuple{N,Int}\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"They must implement the following functions:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"SymbolicIndexingInterface.symbolic_type(::Type{MySym}) = ScalarSymbolic()\nSymbolicIndexingInterface.hasname(::MySym) = true\nSymbolicIndexingInterface.getname(sym::MySym) = sym.name\n\nSymbolicIndexingInterface.symbolic_type(::Type{<:MySymArr}) = ArraySymbolic()\nSymbolicIndexingInterface.hasname(::MySymArr) = true\nSymbolicIndexingInterface.getname(sym::MySymArr) = sym.name\nfunction Base.collect(sym::MySymArr)\n [\n MySym(Symbol(sym.name, :_, join(idxs, \"_\")))\n for idxs in Iterators.product(Base.OneTo.(sym.size)...)\n ]\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"hasname is not required to always be true for symbolic types. For example, Symbolics.Num returns false whenever the wrapped value is a number, or an expression.","category":"page"},{"location":"api/#Interface-Functions","page":"API","title":"Interface Functions","text":"","category":"section"},{"location":"api/#Mandatory-methods","page":"API","title":"Mandatory methods","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"symbolic_container\nis_variable\nvariable_index\nvariable_symbols\nis_parameter\nparameter_index\nparameter_symbols\nis_independent_variable\nindependent_variable_symbols\nis_observed\nis_time_dependent\nconstant_structure\nall_variable_symbols\nall_symbols\nsolvedvariables\nallvariables","category":"page"},{"location":"api/#SymbolicIndexingInterface.symbolic_container","page":"API","title":"SymbolicIndexingInterface.symbolic_container","text":"symbolic_container(p)\n\nUsing p, return an object that implements the symbolic indexing interface. In case p itself implements the interface, p can be returned as-is. All symbolic indexing interface methods fall back to calling the same method on symbolic_container(p), so this may be used for trivial implementations of the interface that forward all calls to another object.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_variable","page":"API","title":"SymbolicIndexingInterface.is_variable","text":"is_variable(sys, sym)\n\nCheck whether the given sym is a variable in sys.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.variable_index","page":"API","title":"SymbolicIndexingInterface.variable_index","text":"variable_index(sys, sym, [i])\n\nReturn the index of the given variable sym in sys, or nothing otherwise. If constant_structure is false, this accepts the current time index as an additional parameter i.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.variable_symbols","page":"API","title":"SymbolicIndexingInterface.variable_symbols","text":"variable_symbols(sys, [i])\n\nReturn a vector of the symbolic variables being solved for in the system sys. If constant_structure(sys) == false this accepts an additional parameter indicating the current time index. The returned vector should not be mutated.\n\nFor types that implement Base.getindex with symbolic indices using this interface, the shorthand sys[solvedvariables] can be used as shorthand for sys[variable_symbols(sys)]. See: solvedvariables.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_parameter","page":"API","title":"SymbolicIndexingInterface.is_parameter","text":"is_parameter(sys, sym)\n\nCheck whether the given sym is a parameter in sys.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.parameter_index","page":"API","title":"SymbolicIndexingInterface.parameter_index","text":"parameter_index(sys, sym)\n\nReturn the index of the given parameter sym in sys, or nothing otherwise.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.parameter_symbols","page":"API","title":"SymbolicIndexingInterface.parameter_symbols","text":"parameter_symbols(sys)\n\nReturn a vector of the symbolic parameters of the given system sys. The returned vector should not be mutated.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_independent_variable","page":"API","title":"SymbolicIndexingInterface.is_independent_variable","text":"is_independent_variable(sys, sym)\n\nCheck whether the given sym is an independent variable in sys. The returned vector should not be mutated.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.independent_variable_symbols","page":"API","title":"SymbolicIndexingInterface.independent_variable_symbols","text":"independent_variable_symbols(sys)\n\nReturn a vector of the symbolic independent variables of the given system sys.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_observed","page":"API","title":"SymbolicIndexingInterface.is_observed","text":"is_observed(sys, sym)\n\nCheck whether the given sym is an observed value in sys.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_time_dependent","page":"API","title":"SymbolicIndexingInterface.is_time_dependent","text":"is_time_dependent(sys)\n\nCheck if sys has time as (one of) its independent variables.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.constant_structure","page":"API","title":"SymbolicIndexingInterface.constant_structure","text":"constant_structure(sys)\n\nCheck if sys has a constant structure. Constant structure systems do not change the number of variables or parameters over time.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.all_variable_symbols","page":"API","title":"SymbolicIndexingInterface.all_variable_symbols","text":"all_variable_symbols(sys)\n\nReturn a vector of variable symbols in the system, including observed quantities.\n\nFor types that implement Base.getindex with symbolic indices using this interface, The shorthand sys[allvariables] can be used as shorthand for sys[all_variable_symbols(sys)]. See: allvariables.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.all_symbols","page":"API","title":"SymbolicIndexingInterface.all_symbols","text":"all_symbols(sys)\n\nReturn an array of all symbols in the system. This includes parameters and independent variables.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.solvedvariables","page":"API","title":"SymbolicIndexingInterface.solvedvariables","text":"const solvedvariables = SolvedVariables()\n\nThis singleton is used as a shortcut to allow indexing of all solution variables (excluding observed quantities). It has a symbolic_type of ScalarSymbolic. See: variable_symbols.\n\n\n\n\n\n","category":"constant"},{"location":"api/#SymbolicIndexingInterface.allvariables","page":"API","title":"SymbolicIndexingInterface.allvariables","text":"const allvariables = AllVariables()\n\nThis singleton is used as a shortcut to allow indexing of all solution variables (including observed quantities). It has a symbolic_type of ScalarSymbolic. See all_variable_symbols.\n\n\n\n\n\n","category":"constant"},{"location":"api/#Optional-Methods","page":"API","title":"Optional Methods","text":"","category":"section"},{"location":"api/#Observed-equation-handling","page":"API","title":"Observed equation handling","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"observed","category":"page"},{"location":"api/#SymbolicIndexingInterface.observed","page":"API","title":"SymbolicIndexingInterface.observed","text":"observed(sys, sym, [states])\n\nReturn the observed function of the given sym in sys. The returned function should have the signature (u, p) -> [values...] where u and p is the current state and parameter vector, respectively. If istimedependent(sys) == true, the function should accept the current time t as its third parameter. If constant_structure(sys) == false, accept 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.\n\nSee also: is_time_dependent, constant_structure\n\n\n\n\n\n","category":"function"},{"location":"api/#Parameter-indexing","page":"API","title":"Parameter indexing","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"parameter_values\nset_parameter!\ngetp\nsetp","category":"page"},{"location":"api/#SymbolicIndexingInterface.parameter_values","page":"API","title":"SymbolicIndexingInterface.parameter_values","text":"parameter_values(p)\n\nReturn an indexable collection containing the value of each parameter in p.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.set_parameter!","page":"API","title":"SymbolicIndexingInterface.set_parameter!","text":"set_parameter!(sys, val, idx)\n\nSet the parameter at index idx to val for system sys. This defaults to modifying parameter_values(sys). 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.\n\nSee: parameter_values\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.getp","page":"API","title":"SymbolicIndexingInterface.getp","text":"getp(sys, p)\n\nReturn a function that takes an integrator or solution of sys, and returns the value of the parameter p. Note that p can be a direct numerical index or a symbolic value. Requires that the integrator or solution implement parameter_values. This function typically does not need to be implemented, and has a default implementation relying on parameter_values.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.setp","page":"API","title":"SymbolicIndexingInterface.setp","text":"setp(sys, p)\n\nReturn a function that takes an integrator of sys and a value, and sets the parameter p to that value. Note that p can be a direct numerical index or a symbolic value. Requires that the integrator implement parameter_values and the returned collection be a mutable reference to the parameter vector in the integrator. 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#State-indexing","page":"API","title":"State indexing","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Timeseries\nNotTimeseries\nis_timeseries\nstate_values\nset_state!\ncurrent_time\ngetu\nsetu","category":"page"},{"location":"api/#SymbolicIndexingInterface.Timeseries","page":"API","title":"SymbolicIndexingInterface.Timeseries","text":"struct Timeseries <: IsTimeseriesTrait end\n\nTrait indicating a type contains timeseries data. This affects the behaviour of functions such as state_values and current_time.\n\nSee also: NotTimeseries, is_timeseries\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.NotTimeseries","page":"API","title":"SymbolicIndexingInterface.NotTimeseries","text":"struct NotTimeseries <: IsTimeseriesTrait end\n\nTrait 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.\n\nSee also: Timeseries, is_timeseries\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.is_timeseries","page":"API","title":"SymbolicIndexingInterface.is_timeseries","text":"is_timeseries(x) = is_timeseries(typeof(x))\nis_timeseries(::Type)\n\nGet the timeseries trait of a type. Defaults to NotTimeseries for all types.\n\nSee also: Timeseries, NotTimeseries\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.state_values","page":"API","title":"SymbolicIndexingInterface.state_values","text":"state_values(p)\n\nReturn an indexable collection containing the values of all states in the integrator or problem p. If is_timeseries(p) is Timeseries, return a vector of arrays, each of which contain the state values at the corresponding timestep.\n\nSee: is_timeseries\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.set_state!","page":"API","title":"SymbolicIndexingInterface.set_state!","text":"set_state!(sys, val, idx)\n\nSet the state at index idx to val for system sys. This defaults to modifying state_values(sys). 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 setu.\n\nSee: state_values\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.current_time","page":"API","title":"SymbolicIndexingInterface.current_time","text":"current_time(p)\n\nReturn the current time in the integrator or problem p. If is_timeseries(p) is Timeseries, return the vector of timesteps at which the state value is saved.\n\nSee: is_timeseries\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.getu","page":"API","title":"SymbolicIndexingInterface.getu","text":"getu(sys, sym)\n\nReturn a function that takes an integrator, problem or solution of sys, and returns the value of the symbolic sym. sym can be a direct index into the state vector, a symbolic state, a symbolic expression involving symbolic quantities in the system sys, or an array/tuple of the aforementioned.\n\nAt minimum, this requires that the integrator, problem or solution implement state_values. To support symbolic expressions, the integrator or problem must implement observed, parameter_values and current_time.\n\nThis function typically does not need to be implemented, and has a default implementation relying on the above functions.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.setu","page":"API","title":"SymbolicIndexingInterface.setu","text":"setu(sys, sym)\n\nReturn a function that takes an integrator or problem of sys and a value, and sets the the state sym to that value. Note that sym can be a direct numerical index, a symbolic state, or an array/tuple of the aforementioned.\n\nRequires that the integrator implement state_values and the returned collection be a mutable reference to the state vector in the integrator/problem. 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#Symbolic-Trait","page":"API","title":"Symbolic Trait","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"ScalarSymbolic\nArraySymbolic\nNotSymbolic\nsymbolic_type\nhasname\ngetname","category":"page"},{"location":"api/#SymbolicIndexingInterface.ScalarSymbolic","page":"API","title":"SymbolicIndexingInterface.ScalarSymbolic","text":"struct ScalarSymbolic <: SymbolicTypeTrait end\n\nTrait indicating a type is a scalar symbolic variable.\n\nSee also: ArraySymbolic, NotSymbolic, symbolic_type\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.ArraySymbolic","page":"API","title":"SymbolicIndexingInterface.ArraySymbolic","text":"struct ArraySymbolic <: SymbolicTypeTrait end\n\nTrait 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.\n\nSee also: ScalarSymbolic, NotSymbolic, symbolic_type\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.NotSymbolic","page":"API","title":"SymbolicIndexingInterface.NotSymbolic","text":"struct NotSymbolic <: SymbolicTypeTrait end\n\nTrait indicating a type is not symbolic.\n\nSee also: ScalarSymbolic, ArraySymbolic, symbolic_type\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.symbolic_type","page":"API","title":"SymbolicIndexingInterface.symbolic_type","text":"symbolic_type(x) = symbolic_type(typeof(x))\nsymbolic_type(::Type)\n\nGet the symbolic type trait of a type. Default to NotSymbolic for all types except Symbol and Expr, both of which are ScalarSymbolic.\n\nSee also: ScalarSymbolic, ArraySymbolic, NotSymbolic\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.hasname","page":"API","title":"SymbolicIndexingInterface.hasname","text":"hasname(x)\n\nCheck whether the given symbolic variable (for which symbolic_type(x) != NotSymbolic()) has a valid name as per getname.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.getname","page":"API","title":"SymbolicIndexingInterface.getname","text":"getname(x)::Symbol\n\nGet the name of a symbolic variable as a Symbol\n\n\n\n\n\n","category":"function"},{"location":"api/#Types","page":"API","title":"Types","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"SymbolCache","category":"page"},{"location":"api/#SymbolicIndexingInterface.SymbolCache","page":"API","title":"SymbolicIndexingInterface.SymbolCache","text":"struct SymbolCache{V,P,I}\nfunction SymbolCache(vars, [params, [indepvars]])\n\nA struct implementing the symbolic indexing interface for the trivial case of having a vector of variables, parameters, and independent variables. This struct does not implement observed, and is_observed returns false for all input symbols. It is considered time dependent if it contains at least one independent variable.\n\nThe 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.\n\n\n\n\n\n","category":"type"},{"location":"usage/#Using-the-SciML-Symbolic-Indexing-Interface","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"","category":"section"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"This tutorial will cover ways to use the symbolic indexing interface for types that implement it. SciML's core types (problems, solutions, and iterator (integrator) types) all support this symbolic indexing interface which allows for domain-specific interfaces (such as ModelingToolkit, Catalyst, etc.) to seamlessly blend their symbolic languages with the types obtained from SciML. Other tutorials will focus on how users can make use of the interface for their own DSL, this tutorial will simply focus on what the user experience looks like for DSL which have set it up.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"We recommend any DSL implementing the symbolic indexing interface to link to this tutorial as a full description of the functionality.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"note: Note\nWhile this tutorial focuses on demonstrating the symbolic indexing interface for ODEs, note that the same functionality works across all of the other problem types, such as optimization problems, nonlinear problems, nonlinear solutions, etc.","category":"page"},{"location":"usage/#Symbolic-Indexing-of-Differential-Equation-Solutions","page":"Using the SciML Symbolic Indexing Interface","title":"Symbolic Indexing of Differential Equation Solutions","text":"","category":"section"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Consider the following example:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"using ModelingToolkit, OrdinaryDiffEq, SymbolicIndexingInterface, Plots\n\n@parameters σ ρ β\n@variables t x(t) y(t) z(t) w(t)\nD = Differential(t)\n\neqs = [D(D(x)) ~ σ * (y - x),\n D(y) ~ x * (ρ - z) - y,\n D(z) ~ x * y - β * z,\n w ~ x + y + z]\n\n@named sys = ODESystem(eqs)\nsys = structural_simplify(sys)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"The system has 4 state variables, 3 parameters, and one observed variable:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"ModelingToolkit.observed(sys)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Solving the system,","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"u0 = [D(x) => 2.0,\n x => 1.0,\n y => 0.0,\n z => 0.0]\n\np = [σ => 28.0,\n ρ => 10.0,\n β => 8 / 3]\n\ntspan = (0.0, 100.0)\nprob = ODEProblem(sys, u0, tspan, p, jac = true)\nsol = solve(prob, Tsit5())","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"We can obtain the timeseries of any time-dependent variable using getindex","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[x]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"This also works for arrays or tuples of variables, including observed quantities and independent variables, for interpolating solutions, and plotting:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[[x, y]]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[(t, w)]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol(1.3, idxs=x)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol(1.3, idxs=[x, w])","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol(1.3, idxs=[:y, :z])","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"plot(sol, idxs=x)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"If necessary, Symbols can be used to refer to variables. This is only valid for symbolic variables for which hasname returns true. The Symbol used must match the one returned by getname for the variable.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"hasname(x)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"getname(x)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[(:x, :w)]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Note how when indexing with an array, the returned type is a Vector{Array{Float64}}, and when using a Tuple, the returned type is Vector{Tuple{Float64, Float64}}. To obtain the value of all state variables, we can use the shorthand:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[solvedvariables] # equivalent to sol[variable_symbols(sol)]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"This does not include the observed variable w. To include observed variables in the output, the following shorthand is used:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[allvariables] # equivalent to sol[all_variable_symbols(sol)]","category":"page"},{"location":"usage/#Parameter-Indexing:-Getting-and-Setting-Parameter-Values","page":"Using the SciML Symbolic Indexing Interface","title":"Parameter Indexing: Getting and Setting Parameter Values","text":"","category":"section"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Parameters cannot be obtained using this syntax, and instead require using getp and setp.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"note: Note\nThe reason why parameters use a separate syntax is to be able to ensure type stability of the sol[x] indexing. Without separating the parameter indexing, the return type of symbolic indexing could be anything a parameter can be, which is general is not the same type as state variables!","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"σ_getter = getp(sys, σ)\nσ_getter(sol) # can also pass `prob`","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Note that this also supports arrays/tuples of parameter symbols:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"σ_ρ_getter = getp(sys, (σ, ρ))\nσ_ρ_getter(sol)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Now suppose the system has to be solved with a different value of the parameter β.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"β_setter = setp(sys, β)\nβ_setter(prob, 3)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"The updated parameter values can be checked using parameter_values.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"parameter_values(prob)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"When solving the new system, note that the parameter getter functions still work on the new solution object.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol2 = solve(prob, Tsit5())\nσ_getter(sol)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"σ_ρ_getter(sol)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"To set the entire parameter vector at once, parameter_values can be used (note the usage of broadcasted assignment).","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"parameter_values(prob) .= [29.0, 11.0, 2.5]\nparameter_values(prob)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"note: Note\nThese getters and setters generate high-performance functions for the specific chosen symbols or collection of symbols. Caching the getter/setter function and reusing it on other problem/solution instances can be the key to achieving good performance. Note that this caching is allowed only when the symbolic system is unchanged (it's fine for the numerical values to have changed, but not the underlying equations).","category":"page"},{"location":"simple_sii_sys/#Simple-Demonstration-of-a-Symbolic-System-Structure","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"","category":"section"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"In this tutorial we will show how to implement a system structure type for defining the symbolic indexing of a domain-specific language. This tutorial will show how the SymbolCache type is defined to take in arrays of symbols for its independent, dependent, and parameter variable names and uses that to define the symbolic indexing interface.","category":"page"},{"location":"simple_sii_sys/#Defining-the-ODE","page":"Simple Demonstration of a Symbolic System Structure","title":"Defining the ODE","text":"","category":"section"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"For this example, we will use the Robertson equations:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"beginaligned\nfracdy_1dt = -004y₁ + 10^4 y_2 y_3 \nfracdy_2dt = 004 y_1 - 10^4 y_2 y_3 - 3*10^7 y_2^2 \nfracdy_3dt = 3*10^7 y_2^2 \nendaligned","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"The in-place function for this ODE system can be defined as:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"function rober!(du, u, p, t)\n y₁, y₂, y₃ = u\n k₁, k₂, k₃ = p\n du[1] = -k₁ * y₁ + k₃ * y₂ * y₃\n du[2] = k₁ * y₁ - k₂ * y₂^2 - k₃ * y₂ * y₃\n du[3] = k₂ * y₂^2\n nothing\nend","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"To add symbolic names for the states in this example, a SymbolCache can be created and passed as the sys keyword argument to the ODEFunction constructor, as shown below:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"using OrdinaryDiffEq, SymbolicIndexingInterface\n\nsys = SymbolCache([:y₁, :y₂, :y₃])\nodefun = ODEFunction(rober!; sys = sys)\nnothing # hide","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"This is then used to create and solve the ODEProblem","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"prob = ODEProblem(odefun, [1.0, 0.0, 0.0], (0.0, 1e5), [0.04, 3e7, 1e4])\nsol = solve(prob, Rosenbrock23())","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"The solution can now be indexed symbolically:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"sol[:y₁]","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"sol(1e3, idxs=:y₁)","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"However, we did not give names to the parameters or the independent variables. They can be specified using SymbolCache as well:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"sys = SymbolCache([:y₁, :y₂, :y₃], [:k₁, :k₂, :k₃], :t)\nodefun = ODEFunction(rober!; sys = sys)\nprob = ODEProblem(odefun, [1.0, 0.0, 0.0], (0.0, 1e5), [0.04, 3e7, 1e4])\nsol = solve(prob, Rosenbrock23())\ngetk1 = getp(sys, :k₁)\n\ngetk1(prob)","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"getk1(sol)","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"sol[:t]","category":"page"},{"location":"solution_wrappers/#Defining-Solution-Wrapper-Fallbacks","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"","category":"section"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"The simplest case is when the type contains an object that already implements the interface. All its methods can simply be forwarded to that object. To do so, SymbolicIndexingInterface.jl provides the symbolic_container method. For example,","category":"page"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"struct MySolutionWrapper{T<:SciMLBase.AbstractTimeseriesSolution}\n sol::T\n # other properties...\nend\n\nsymbolic_container(sys::MySolutionWrapper) = sys.sol","category":"page"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"MySolutionWrapper wraps an AbstractTimeseriesSolution which already implements the interface. Since symbolic_container will return the wrapped solution, all method calls such as is_parameter(sys::MySolutionWrapper, sym) will be forwarded to is_parameter(sys.sol, sym).","category":"page"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"In cases where some methods need to function differently than those of the wrapped type, they can be selectively defined. For example, suppose MySolutionWrapper does not support observed quantities. The following method can be defined (in addition to the one above):","category":"page"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"is_observed(sys::MySolutionWrapper, sym) = false","category":"page"},{"location":"#SymbolicIndexingInterface.jl:-Standardized-Symbolic-Indexing-of-Julia","page":"Home","title":"SymbolicIndexingInterface.jl: Standardized Symbolic Indexing of Julia","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"SymbolicIndexingInterface.jl is a set of interface functions for handling containers of symbolic variables.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To install SymbolicIndexingInterface.jl, use the Julia package manager:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"SymbolicIndexingInterface\")","category":"page"},{"location":"#Introduction","page":"Home","title":"Introduction","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"The symbolic indexing interface has 2 levels:","category":"page"},{"location":"","page":"Home","title":"Home","text":"The user level. At the user level, a modeler or engineer simply uses terms from a domain-specific language (DSL) inside of SciML functionality and will receive the requested values. For example, if a DSL defines a symbol x, then sol[x] returns the solution value(s) for x.\nThe DSL system structure level. This is the structure which defines the symbolic indexing for a given problem/solution. DSLs can tag a constructed problem/solution with this object in order to endow the SciML tools with the ability to index symbolically according to the definitions the DSL writer wants.","category":"page"},{"location":"#Contributing","page":"Home","title":"Contributing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Please refer to the SciML ColPrac: Contributor's Guide on Collaborative Practices for Community Packages for guidance on PRs, issues, and other matters relating to contributing to SciML.\nThere are a few community forums:\nthe #diffeq-bridged channel in the Julia Slack\nJuliaDiffEq on Gitter\non the Julia Discourse forums\nsee also SciML Community page","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"
The documentation of this SciML package was built using these direct dependencies,","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
and using this machine and Julia version.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
A more complete overview of all dependencies and their versions is also provided.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"using TOML\nusing Markdown\nversion = TOML.parse(read(\"../../Project.toml\", String))[\"version\"]\nname = TOML.parse(read(\"../../Project.toml\", String))[\"name\"]\nlink_manifest = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Manifest.toml\"\nlink_project = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Project.toml\"\nMarkdown.parse(\"\"\"You can also download the\n[manifest]($link_manifest)\nfile and the\n[project]($link_project)\nfile.\n\"\"\")","category":"page"}] +[{"location":"complete_sii/#Implementing-the-Complete-Symbolic-Indexing-Interface","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"This tutorial will show how to define the entire Symbolic Indexing Interface on an ExampleSystem:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"struct ExampleSystem\n state_index::Dict{Symbol,Int}\n parameter_index::Dict{Symbol,Int}\n independent_variable::Union{Symbol,Nothing}\n # mapping from observed variable to Expr to calculate its value\n observed::Dict{Symbol,Expr}\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Not all the methods in the interface are required. Some only need to be implemented if a type supports specific functionality. Consider the following struct, which needs to implement the interface:","category":"page"},{"location":"complete_sii/#Mandatory-methods","page":"Implementing the Complete Symbolic Indexing Interface","title":"Mandatory methods","text":"","category":"section"},{"location":"complete_sii/#Simple-Indexing-Functions","page":"Implementing the Complete Symbolic Indexing Interface","title":"Simple Indexing Functions","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"These are the simple functions which describe how to turn symbols into indices.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"function SymbolicIndexingInterface.is_variable(sys::ExampleSystem, sym)\n haskey(sys.state_index, sym)\nend\n\nfunction SymbolicIndexingInterface.variable_index(sys::ExampleSystem, sym)\n get(sys.state_index, sym, nothing)\nend\n\nfunction SymbolicIndexingInterface.variable_symbols(sys::ExampleSystem)\n collect(keys(sys.state_index))\nend\n\nfunction SymbolicIndexingInterface.is_parameter(sys::ExampleSystem, sym)\n haskey(sys.parameter_index, sym)\nend\n\nfunction SymbolicIndexingInterface.parameter_index(sys::ExampleSystem, sym)\n get(sys.parameter_index, sym, nothing)\nend\n\nfunction SymbolicIndexingInterface.parameter_symbols(sys::ExampleSystem)\n collect(keys(sys.parameter_index))\nend\n\nfunction SymbolicIndexingInterface.is_independent_variable(sys::ExampleSystem, sym)\n # note we have to check separately for `nothing`, otherwise\n # `is_independent_variable(p, nothing)` would return `true`.\n sys.independent_variable !== nothing && sym === sys.independent_variable\nend\n\nfunction SymbolicIndexingInterface.independent_variable_symbols(sys::ExampleSystem)\n sys.independent_variable === nothing ? [] : [sys.independent_variable]\nend\n\nfunction SymbolicIndexingInterface.is_time_dependent(sys::ExampleSystem)\n sys.independent_variable !== nothing\nend\n\nSymbolicIndexingInterface.constant_structure(::ExampleSystem) = true\n\nfunction SymbolicIndexingInterface.all_solvable_symbols(sys::ExampleSystem)\n return vcat(\n collect(keys(sys.state_index)),\n collect(keys(sys.observed)),\n )\nend\n\nfunction SymbolicIndexingInterface.all_symbols(sys::ExampleSystem)\n return vcat(\n all_solvable_symbols(sys),\n collect(keys(sys.parameter_index)),\n sys.independent_variable === nothing ? Symbol[] : sys.independent_variable\n )\nend","category":"page"},{"location":"complete_sii/#Observed-Equation-Handling","page":"Implementing the Complete Symbolic Indexing Interface","title":"Observed Equation Handling","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"These are for handling symbolic expressions and generating equations which are not directly in the solution vector.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"using RuntimeGeneratedFunctions\nRuntimeGeneratedFunctions.init(@__MODULE__)\n\n# this type accepts `Expr` for observed expressions involving state/parameter/observed\n# variables\nSymbolicIndexingInterface.is_observed(sys::ExampleSystem, sym) = sym isa Expr || sym isa Symbol && haskey(sys.observed, sym)\n\nfunction SymbolicIndexingInterface.observed(sys::ExampleSystem, sym::Expr)\n # generate a function with the appropriate signature\n if is_time_dependent(sys)\n fn_expr = :(\n function gen(u, p, t)\n # assign a variable for each state symbol it's value in u\n $([:($var = u[$idx]) for (var, idx) in pairs(sys.state_index)]...)\n # assign a variable for each parameter symbol it's value in p\n $([:($var = p[$idx]) for (var, idx) in pairs(sys.parameter_index)]...)\n # assign a variable for the independent variable\n $(sys.independent_variable) = t\n # return the value of the expression\n return $sym\n end\n )\n else\n fn_expr = :(\n function gen(u, p)\n # assign a variable for each state symbol it's value in u\n $([:($var = u[$idx]) for (var, idx) in pairs(sys.state_index)]...)\n # assign a variable for each parameter symbol it's value in p\n $([:($var = p[$idx]) for (var, idx) in pairs(sys.parameter_index)]...)\n # return the value of the expression\n return $sym\n end\n )\n end\n return @RuntimeGeneratedFunction(fn_expr)\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"In case a type does not support such observed quantities, is_observed must be defined to always return false, and observed does not need to be implemented.","category":"page"},{"location":"complete_sii/#Note-about-constant-structure","page":"Implementing the Complete Symbolic Indexing Interface","title":"Note about constant structure","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Note that the method definitions are all assuming constant_structure(p) == true.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"In case constant_structure(p) == false, the following methods would change:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"constant_structure(::ExampleSystem) = false\nvariable_index(sys::ExampleSystem, sym) would become variable_index(sys::ExampleSystem, sym i) where i is the time index at which the index of sym is required.\nvariable_symbols(sys::ExampleSystem) would become variable_symbols(sys::ExampleSystem, i) where i is the time index at which the variable symbols are required.\nobserved(sys::ExampleSystem, sym) would become observed(sys::ExampleSystem, sym, i) where i is either the time index at which the index of sym is required or a Vector of state symbols at the current time index.","category":"page"},{"location":"complete_sii/#Optional-methods","page":"Implementing the Complete Symbolic Indexing Interface","title":"Optional methods","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Note that observed is optional if is_observed is always false, or the type is only responsible for identifying observed values and observed will always be called on a type that wraps this type. An example is ModelingToolkit.AbstractSystem, which can identify whether a value is observed, but cannot implement observed itself.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Other optional methods relate to indexing functions. If a type contains the values of parameter variables, it must implement parameter_values. This allows the default definitions of getp and setp to work. While setp is not typically useful for solution objects, it may be useful for integrators. Typically, the default implementations for getp and setp will suffice, and manually defining them is not necessary.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"function SymbolicIndexingInterface.parameter_values(sys::ExampleSystem)\n sys.p\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"If a type contains the value of state variables, it can define state_values to enable the usage of getu and setu. These methods retturn getter/ setter functions to access or update the value of a state variable (or a collection of them). If the type also supports generating observed functions, getu also enables returning functions to access the value of arbitrary expressions involving the system's symbols. This also requires that the type implement parameter_values and current_time (if the system is time-dependent).","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Consider the following ExampleIntegrator","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"mutable struct ExampleIntegrator\n u::Vector{Float64}\n p::Vector{Float64}\n t::Float64\n sys::ExampleSystem\nend\n\n# define a fallback for the interface methods\nSymbolicIndexingInterface.symbolic_container(integ::ExampleIntegrator) = integ.sys\nSymbolicIndexingInterface.state_values(sys::ExampleIntegrator) = sys.u\nSymbolicIndexingInterface.parameter_values(sys::ExampleIntegrator) = sys.p\nSymbolicIndexingInterface.current_time(sys::ExampleIntegrator) = sys.t","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Then the following example would work:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"sys = ExampleSystem(Dict(:x => 1, :y => 2, :z => 3), Dict(:a => 1, :b => 2), :t, Dict())\nintegrator = ExampleIntegrator([1.0, 2.0, 3.0], [4.0, 5.0], 6.0, sys)\ngetx = getu(sys, :x)\ngetx(integrator) # 1.0\n\nget_expr = getu(sys, :(x + y + t))\nget_expr(integrator) # 13.0\n\nsetx! = setu(sys, :y)\nsetx!(integrator, 0.0)\ngetx(integrator) # 0.0","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"In case a type stores timeseries data (such as solutions), then it must also implement the Timeseries trait. The type would then return a timeseries from state_values and current_time and the function returned from getu would then return a timeseries as well. For example, consider the ExampleSolution below:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"struct ExampleSolution\n u::Vector{Vector{Float64}}\n t::Vector{Float64}\n p::Vector{Float64}\n sys::ExampleSystem\nend\n\n# define a fallback for the interface methods\nSymbolicIndexingInterface.symbolic_container(sol::ExampleSolution) = sol.sys\nSymbolicIndexingInterface.parameter_values(sol::ExampleSolution) = sol.p\n# define the trait\nSymbolicIndexingInterface.is_timeseries(::Type{ExampleSolution}) = Timeseries()\n# both state_values and current_time return a timeseries, which must be\n# the same length\nSymbolicIndexingInterface.state_values(sol::ExampleSolution) = sol.u\nSymbolicIndexingInterface.current_time(sol::ExampleSolution) = sol.t","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Then the following example would work:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"# using the same system that the ExampleIntegrator used\nsol = ExampleSolution([[1.0, 2.0, 3.0], [1.5, 2.5, 3.5]], [4.0, 5.0], [6.0, 7.0], sys)\ngetx = getu(sys, :x)\ngetx(sol) # [1.0, 1.5]\n\nget_expr = getu(sys, :(x + y + t))\nget_expr(sol) # [9.0, 11.0]\n\nget_arr = getu(sys, [:y, :(x + a)])\nget_arr(sol) # [[2.0, 5.0], [2.5, 5.5]]\n\nget_tuple = getu(sys, (:z, :(z * t)))\nget_tuple(sol) # [(3.0, 18.0), (3.5, 24.5)]","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"Note that setu is not designed to work for Timeseries objects.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"If a type needs to perform some additional actions when updating the state/parameters or if it is not possible to return a mutable reference to the state/parameter vector which can directly be modified, the functions set_state! and/or set_parameter! can be used. For example, suppose our ExampleIntegrator had an additional field u_modified::Bool to allow it to keep track of when a discontinuity occurs and handle it appropriately. This flag needs to be set to true whenever the state is modified. The set_state! function can then be implemented as follows:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"function SymbolicIndexingInterface.set_state!(integrator::ExampleIntegrator, val, idx)\n integrator.u[idx] = val\n integrator.u_modified = true\nend","category":"page"},{"location":"complete_sii/#The-ParameterIndexingProxy","page":"Implementing the Complete Symbolic Indexing Interface","title":"The ParameterIndexingProxy","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"ParameterIndexingProxy is a wrapper around another type which implements the interface and allows using getp and setp to get and set parameter values. This allows for a cleaner interface for parameter indexing. Consider the following example for ExampleIntegrator:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"function Base.getproperty(obj::ExampleIntegrator, sym::Symbol)\n if sym === :ps\n return ParameterIndexingProxy(obj)\n else\n return getfield(obj, sym)\n end\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"This enables the following API:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"integrator = ExampleIntegrator([1.0, 2.0, 3.0], [4.0, 5.0], 6.0, Dict(:x => 1, :y => 2, :z => 3), Dict(:a => 1, :b => 2), :t)\n\nintegrator.ps[:a] # 4.0\ngetp(integrator, :a)(integrator) # functionally the same as above\n\nintegrator.ps[:b] = 3.0\nsetp(integrator, :b)(integrator, 3.0) # functionally the same as above","category":"page"},{"location":"complete_sii/#Implementing-the-SymbolicTypeTrait-for-a-type","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the SymbolicTypeTrait for a type","text":"","category":"section"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"The SymbolicTypeTrait is used to identify values that can act as symbolic variables. It has three variants:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"NotSymbolic for quantities that are not symbolic. This is the default for all types.\nScalarSymbolic for quantities that are symbolic, and represent a single logical value.\nArraySymbolic for quantities that are symbolic, and represent an array of values. Types implementing this trait must return an array of ScalarSymbolic variables of the appropriate size and dimensions when collected.","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"The trait is implemented through the symbolic_type function. Consider the following example types:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"struct MySym\n name::Symbol\nend\n\nstruct MySymArr{N}\n name::Symbol\n size::NTuple{N,Int}\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"They must implement the following functions:","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"SymbolicIndexingInterface.symbolic_type(::Type{MySym}) = ScalarSymbolic()\nSymbolicIndexingInterface.hasname(::MySym) = true\nSymbolicIndexingInterface.getname(sym::MySym) = sym.name\n\nSymbolicIndexingInterface.symbolic_type(::Type{<:MySymArr}) = ArraySymbolic()\nSymbolicIndexingInterface.hasname(::MySymArr) = true\nSymbolicIndexingInterface.getname(sym::MySymArr) = sym.name\nfunction Base.collect(sym::MySymArr)\n [\n MySym(Symbol(sym.name, :_, join(idxs, \"_\")))\n for idxs in Iterators.product(Base.OneTo.(sym.size)...)\n ]\nend","category":"page"},{"location":"complete_sii/","page":"Implementing the Complete Symbolic Indexing Interface","title":"Implementing the Complete Symbolic Indexing Interface","text":"hasname is not required to always be true for symbolic types. For example, Symbolics.Num returns false whenever the wrapped value is a number, or an expression.","category":"page"},{"location":"api/#Interface-Functions","page":"API","title":"Interface Functions","text":"","category":"section"},{"location":"api/#Mandatory-methods","page":"API","title":"Mandatory methods","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"symbolic_container\nis_variable\nvariable_index\nvariable_symbols\nis_parameter\nparameter_index\nparameter_symbols\nis_independent_variable\nindependent_variable_symbols\nis_observed\nis_time_dependent\nconstant_structure\nall_variable_symbols\nall_symbols\nsolvedvariables\nallvariables","category":"page"},{"location":"api/#SymbolicIndexingInterface.symbolic_container","page":"API","title":"SymbolicIndexingInterface.symbolic_container","text":"symbolic_container(p)\n\nUsing p, return an object that implements the symbolic indexing interface. In case p itself implements the interface, p can be returned as-is. All symbolic indexing interface methods fall back to calling the same method on symbolic_container(p), so this may be used for trivial implementations of the interface that forward all calls to another object.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_variable","page":"API","title":"SymbolicIndexingInterface.is_variable","text":"is_variable(sys, sym)\n\nCheck whether the given sym is a variable in sys.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.variable_index","page":"API","title":"SymbolicIndexingInterface.variable_index","text":"variable_index(sys, sym, [i])\n\nReturn the index of the given variable sym in sys, or nothing otherwise. If constant_structure is false, this accepts the current time index as an additional parameter i.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.variable_symbols","page":"API","title":"SymbolicIndexingInterface.variable_symbols","text":"variable_symbols(sys, [i])\n\nReturn a vector of the symbolic variables being solved for in the system sys. If constant_structure(sys) == false this accepts an additional parameter indicating the current time index. The returned vector should not be mutated.\n\nFor types that implement Base.getindex with symbolic indices using this interface, the shorthand sys[solvedvariables] can be used as shorthand for sys[variable_symbols(sys)]. See: solvedvariables.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_parameter","page":"API","title":"SymbolicIndexingInterface.is_parameter","text":"is_parameter(sys, sym)\n\nCheck whether the given sym is a parameter in sys.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.parameter_index","page":"API","title":"SymbolicIndexingInterface.parameter_index","text":"parameter_index(sys, sym)\n\nReturn the index of the given parameter sym in sys, or nothing otherwise.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.parameter_symbols","page":"API","title":"SymbolicIndexingInterface.parameter_symbols","text":"parameter_symbols(sys)\n\nReturn a vector of the symbolic parameters of the given system sys. The returned vector should not be mutated.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_independent_variable","page":"API","title":"SymbolicIndexingInterface.is_independent_variable","text":"is_independent_variable(sys, sym)\n\nCheck whether the given sym is an independent variable in sys. The returned vector should not be mutated.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.independent_variable_symbols","page":"API","title":"SymbolicIndexingInterface.independent_variable_symbols","text":"independent_variable_symbols(sys)\n\nReturn a vector of the symbolic independent variables of the given system sys.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_observed","page":"API","title":"SymbolicIndexingInterface.is_observed","text":"is_observed(sys, sym)\n\nCheck whether the given sym is an observed value in sys.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.is_time_dependent","page":"API","title":"SymbolicIndexingInterface.is_time_dependent","text":"is_time_dependent(sys)\n\nCheck if sys has time as (one of) its independent variables.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.constant_structure","page":"API","title":"SymbolicIndexingInterface.constant_structure","text":"constant_structure(sys)\n\nCheck if sys has a constant structure. Constant structure systems do not change the number of variables or parameters over time.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.all_variable_symbols","page":"API","title":"SymbolicIndexingInterface.all_variable_symbols","text":"all_variable_symbols(sys)\n\nReturn a vector of variable symbols in the system, including observed quantities.\n\nFor types that implement Base.getindex with symbolic indices using this interface, The shorthand sys[allvariables] can be used as shorthand for sys[all_variable_symbols(sys)]. See: allvariables.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.all_symbols","page":"API","title":"SymbolicIndexingInterface.all_symbols","text":"all_symbols(sys)\n\nReturn an array of all symbols in the system. This includes parameters and independent variables.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.solvedvariables","page":"API","title":"SymbolicIndexingInterface.solvedvariables","text":"const solvedvariables = SolvedVariables()\n\nThis singleton is used as a shortcut to allow indexing of all solution variables (excluding observed quantities). It has a symbolic_type of ScalarSymbolic. See: variable_symbols.\n\n\n\n\n\n","category":"constant"},{"location":"api/#SymbolicIndexingInterface.allvariables","page":"API","title":"SymbolicIndexingInterface.allvariables","text":"const allvariables = AllVariables()\n\nThis singleton is used as a shortcut to allow indexing of all solution variables (including observed quantities). It has a symbolic_type of ScalarSymbolic. See all_variable_symbols.\n\n\n\n\n\n","category":"constant"},{"location":"api/#Optional-Methods","page":"API","title":"Optional Methods","text":"","category":"section"},{"location":"api/#Observed-equation-handling","page":"API","title":"Observed equation handling","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"observed","category":"page"},{"location":"api/#SymbolicIndexingInterface.observed","page":"API","title":"SymbolicIndexingInterface.observed","text":"observed(sys, sym, [states])\n\nReturn the observed function of the given sym in sys. The returned function should have the signature (u, p) -> [values...] where u and p is the current state and parameter vector, respectively. If istimedependent(sys) == true, the function should accept the current time t as its third parameter. If constant_structure(sys) == false, accept 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.\n\nSee also: is_time_dependent, constant_structure\n\n\n\n\n\n","category":"function"},{"location":"api/#Parameter-indexing","page":"API","title":"Parameter indexing","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"parameter_values\nset_parameter!\ngetp\nsetp\nParameterIndexingProxy","category":"page"},{"location":"api/#SymbolicIndexingInterface.parameter_values","page":"API","title":"SymbolicIndexingInterface.parameter_values","text":"parameter_values(p)\n\nReturn an indexable collection containing the value of each parameter in p.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.set_parameter!","page":"API","title":"SymbolicIndexingInterface.set_parameter!","text":"set_parameter!(sys, val, idx)\n\nSet the parameter at index idx to val for system sys. This defaults to modifying parameter_values(sys). 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.\n\nSee: parameter_values\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.getp","page":"API","title":"SymbolicIndexingInterface.getp","text":"getp(sys, p)\n\nReturn a function that takes an integrator or solution of sys, and returns the value of the parameter p. Note that p can be a direct numerical index or a symbolic value. Requires that the integrator or solution implement parameter_values. This function typically does not need to be implemented, and has a default implementation relying on parameter_values.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.setp","page":"API","title":"SymbolicIndexingInterface.setp","text":"setp(sys, p)\n\nReturn a function that takes an integrator of sys and a value, and sets the parameter p to that value. Note that p can be a direct numerical index or a symbolic value. Requires that the integrator implement parameter_values and the returned collection be a mutable reference to the parameter vector in the integrator. 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.ParameterIndexingProxy","page":"API","title":"SymbolicIndexingInterface.ParameterIndexingProxy","text":"struct ParameterIndexingProxy\n\nThis struct wraps any struct implementing the symbolic indexing interface. 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.\n\n\n\n\n\n","category":"type"},{"location":"api/#State-indexing","page":"API","title":"State indexing","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"Timeseries\nNotTimeseries\nis_timeseries\nstate_values\nset_state!\ncurrent_time\ngetu\nsetu","category":"page"},{"location":"api/#SymbolicIndexingInterface.Timeseries","page":"API","title":"SymbolicIndexingInterface.Timeseries","text":"struct Timeseries <: IsTimeseriesTrait end\n\nTrait indicating a type contains timeseries data. This affects the behaviour of functions such as state_values and current_time.\n\nSee also: NotTimeseries, is_timeseries\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.NotTimeseries","page":"API","title":"SymbolicIndexingInterface.NotTimeseries","text":"struct NotTimeseries <: IsTimeseriesTrait end\n\nTrait 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.\n\nSee also: Timeseries, is_timeseries\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.is_timeseries","page":"API","title":"SymbolicIndexingInterface.is_timeseries","text":"is_timeseries(x) = is_timeseries(typeof(x))\nis_timeseries(::Type)\n\nGet the timeseries trait of a type. Defaults to NotTimeseries for all types.\n\nSee also: Timeseries, NotTimeseries\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.state_values","page":"API","title":"SymbolicIndexingInterface.state_values","text":"state_values(p)\n\nReturn an indexable collection containing the values of all states in the integrator or problem p. If is_timeseries(p) is Timeseries, return a vector of arrays, each of which contain the state values at the corresponding timestep.\n\nSee: is_timeseries\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.set_state!","page":"API","title":"SymbolicIndexingInterface.set_state!","text":"set_state!(sys, val, idx)\n\nSet the state at index idx to val for system sys. This defaults to modifying state_values(sys). 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 setu.\n\nSee: state_values\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.current_time","page":"API","title":"SymbolicIndexingInterface.current_time","text":"current_time(p)\n\nReturn the current time in the integrator or problem p. If is_timeseries(p) is Timeseries, return the vector of timesteps at which the state value is saved.\n\nSee: is_timeseries\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.getu","page":"API","title":"SymbolicIndexingInterface.getu","text":"getu(sys, sym)\n\nReturn a function that takes an integrator, problem or solution of sys, and returns the value of the symbolic sym. sym can be a direct index into the state vector, a symbolic state, a symbolic expression involving symbolic quantities in the system sys, or an array/tuple of the aforementioned.\n\nAt minimum, this requires that the integrator, problem or solution implement state_values. To support symbolic expressions, the integrator or problem must implement observed, parameter_values and current_time.\n\nThis function typically does not need to be implemented, and has a default implementation relying on the above functions.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.setu","page":"API","title":"SymbolicIndexingInterface.setu","text":"setu(sys, sym)\n\nReturn a function that takes an integrator or problem of sys and a value, and sets the the state sym to that value. Note that sym can be a direct numerical index, a symbolic state, or an array/tuple of the aforementioned.\n\nRequires that the integrator implement state_values and the returned collection be a mutable reference to the state vector in the integrator/problem. 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#Symbolic-Trait","page":"API","title":"Symbolic Trait","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"ScalarSymbolic\nArraySymbolic\nNotSymbolic\nsymbolic_type\nhasname\ngetname","category":"page"},{"location":"api/#SymbolicIndexingInterface.ScalarSymbolic","page":"API","title":"SymbolicIndexingInterface.ScalarSymbolic","text":"struct ScalarSymbolic <: SymbolicTypeTrait end\n\nTrait indicating a type is a scalar symbolic variable.\n\nSee also: ArraySymbolic, NotSymbolic, symbolic_type\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.ArraySymbolic","page":"API","title":"SymbolicIndexingInterface.ArraySymbolic","text":"struct ArraySymbolic <: SymbolicTypeTrait end\n\nTrait 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.\n\nSee also: ScalarSymbolic, NotSymbolic, symbolic_type\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.NotSymbolic","page":"API","title":"SymbolicIndexingInterface.NotSymbolic","text":"struct NotSymbolic <: SymbolicTypeTrait end\n\nTrait indicating a type is not symbolic.\n\nSee also: ScalarSymbolic, ArraySymbolic, symbolic_type\n\n\n\n\n\n","category":"type"},{"location":"api/#SymbolicIndexingInterface.symbolic_type","page":"API","title":"SymbolicIndexingInterface.symbolic_type","text":"symbolic_type(x) = symbolic_type(typeof(x))\nsymbolic_type(::Type)\n\nGet the symbolic type trait of a type. Default to NotSymbolic for all types except Symbol and Expr, both of which are ScalarSymbolic.\n\nSee also: ScalarSymbolic, ArraySymbolic, NotSymbolic\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.hasname","page":"API","title":"SymbolicIndexingInterface.hasname","text":"hasname(x)\n\nCheck whether the given symbolic variable (for which symbolic_type(x) != NotSymbolic()) has a valid name as per getname.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicIndexingInterface.getname","page":"API","title":"SymbolicIndexingInterface.getname","text":"getname(x)::Symbol\n\nGet the name of a symbolic variable as a Symbol\n\n\n\n\n\n","category":"function"},{"location":"api/#Types","page":"API","title":"Types","text":"","category":"section"},{"location":"api/","page":"API","title":"API","text":"SymbolCache","category":"page"},{"location":"api/#SymbolicIndexingInterface.SymbolCache","page":"API","title":"SymbolicIndexingInterface.SymbolCache","text":"struct SymbolCache{V,P,I}\nfunction SymbolCache(vars, [params, [indepvars]])\n\nA struct implementing the symbolic indexing interface for the trivial case of having a vector of variables, parameters, and independent variables. This struct does not implement observed, and is_observed returns false for all input symbols. It is considered time dependent if it contains at least one independent variable.\n\nThe 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.\n\n\n\n\n\n","category":"type"},{"location":"usage/#Using-the-SciML-Symbolic-Indexing-Interface","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"","category":"section"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"This tutorial will cover ways to use the symbolic indexing interface for types that implement it. SciML's core types (problems, solutions, and iterator (integrator) types) all support this symbolic indexing interface which allows for domain-specific interfaces (such as ModelingToolkit, Catalyst, etc.) to seamlessly blend their symbolic languages with the types obtained from SciML. Other tutorials will focus on how users can make use of the interface for their own DSL, this tutorial will simply focus on what the user experience looks like for DSL which have set it up.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"We recommend any DSL implementing the symbolic indexing interface to link to this tutorial as a full description of the functionality.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"note: Note\nWhile this tutorial focuses on demonstrating the symbolic indexing interface for ODEs, note that the same functionality works across all of the other problem types, such as optimization problems, nonlinear problems, nonlinear solutions, etc.","category":"page"},{"location":"usage/#Symbolic-Indexing-of-Differential-Equation-Solutions","page":"Using the SciML Symbolic Indexing Interface","title":"Symbolic Indexing of Differential Equation Solutions","text":"","category":"section"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Consider the following example:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"using ModelingToolkit, OrdinaryDiffEq, SymbolicIndexingInterface, Plots\n\n@parameters σ ρ β\n@variables t x(t) y(t) z(t) w(t)\nD = Differential(t)\n\neqs = [D(D(x)) ~ σ * (y - x),\n D(y) ~ x * (ρ - z) - y,\n D(z) ~ x * y - β * z,\n w ~ x + y + z]\n\n@named sys = ODESystem(eqs)\nsys = structural_simplify(sys)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"The system has 4 state variables, 3 parameters, and one observed variable:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"ModelingToolkit.observed(sys)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Solving the system,","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"u0 = [D(x) => 2.0,\n x => 1.0,\n y => 0.0,\n z => 0.0]\n\np = [σ => 28.0,\n ρ => 10.0,\n β => 8 / 3]\n\ntspan = (0.0, 100.0)\nprob = ODEProblem(sys, u0, tspan, p, jac = true)\nsol = solve(prob, Tsit5())","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"We can obtain the timeseries of any time-dependent variable using getindex","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[x]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"This also works for arrays or tuples of variables, including observed quantities and independent variables, for interpolating solutions, and plotting:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[[x, y]]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[(t, w)]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol(1.3, idxs=x)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol(1.3, idxs=[x, w])","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol(1.3, idxs=[:y, :z])","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"plot(sol, idxs=x)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"If necessary, Symbols can be used to refer to variables. This is only valid for symbolic variables for which hasname returns true. The Symbol used must match the one returned by getname for the variable.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"hasname(x)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"getname(x)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[(:x, :w)]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Note how when indexing with an array, the returned type is a Vector{Array{Float64}}, and when using a Tuple, the returned type is Vector{Tuple{Float64, Float64}}. To obtain the value of all state variables, we can use the shorthand:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[solvedvariables] # equivalent to sol[variable_symbols(sol)]","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"This does not include the observed variable w. To include observed variables in the output, the following shorthand is used:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol[allvariables] # equivalent to sol[all_variable_symbols(sol)]","category":"page"},{"location":"usage/#Parameter-Indexing:-Getting-and-Setting-Parameter-Values","page":"Using the SciML Symbolic Indexing Interface","title":"Parameter Indexing: Getting and Setting Parameter Values","text":"","category":"section"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Parameters cannot be obtained using this syntax, and instead require using getp and setp.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"note: Note\nThe reason why parameters use a separate syntax is to be able to ensure type stability of the sol[x] indexing. Without separating the parameter indexing, the return type of symbolic indexing could be anything a parameter can be, which is general is not the same type as state variables!","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"σ_getter = getp(sys, σ)\nσ_getter(sol) # can also pass `prob`","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Note that this also supports arrays/tuples of parameter symbols:","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"σ_ρ_getter = getp(sys, (σ, ρ))\nσ_ρ_getter(sol)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"Now suppose the system has to be solved with a different value of the parameter β.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"β_setter = setp(sys, β)\nβ_setter(prob, 3)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"The updated parameter values can be checked using parameter_values.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"parameter_values(prob)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"When solving the new system, note that the parameter getter functions still work on the new solution object.","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"sol2 = solve(prob, Tsit5())\nσ_getter(sol)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"σ_ρ_getter(sol)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"To set the entire parameter vector at once, parameter_values can be used (note the usage of broadcasted assignment).","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"parameter_values(prob) .= [29.0, 11.0, 2.5]\nparameter_values(prob)","category":"page"},{"location":"usage/","page":"Using the SciML Symbolic Indexing Interface","title":"Using the SciML Symbolic Indexing Interface","text":"note: Note\nThese getters and setters generate high-performance functions for the specific chosen symbols or collection of symbols. Caching the getter/setter function and reusing it on other problem/solution instances can be the key to achieving good performance. Note that this caching is allowed only when the symbolic system is unchanged (it's fine for the numerical values to have changed, but not the underlying equations).","category":"page"},{"location":"simple_sii_sys/#Simple-Demonstration-of-a-Symbolic-System-Structure","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"","category":"section"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"In this tutorial we will show how to implement a system structure type for defining the symbolic indexing of a domain-specific language. This tutorial will show how the SymbolCache type is defined to take in arrays of symbols for its independent, dependent, and parameter variable names and uses that to define the symbolic indexing interface.","category":"page"},{"location":"simple_sii_sys/#Defining-the-ODE","page":"Simple Demonstration of a Symbolic System Structure","title":"Defining the ODE","text":"","category":"section"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"For this example, we will use the Robertson equations:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"beginaligned\nfracdy_1dt = -004y₁ + 10^4 y_2 y_3 \nfracdy_2dt = 004 y_1 - 10^4 y_2 y_3 - 3*10^7 y_2^2 \nfracdy_3dt = 3*10^7 y_2^2 \nendaligned","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"The in-place function for this ODE system can be defined as:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"function rober!(du, u, p, t)\n y₁, y₂, y₃ = u\n k₁, k₂, k₃ = p\n du[1] = -k₁ * y₁ + k₃ * y₂ * y₃\n du[2] = k₁ * y₁ - k₂ * y₂^2 - k₃ * y₂ * y₃\n du[3] = k₂ * y₂^2\n nothing\nend","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"To add symbolic names for the states in this example, a SymbolCache can be created and passed as the sys keyword argument to the ODEFunction constructor, as shown below:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"using OrdinaryDiffEq, SymbolicIndexingInterface\n\nsys = SymbolCache([:y₁, :y₂, :y₃])\nodefun = ODEFunction(rober!; sys = sys)\nnothing # hide","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"This is then used to create and solve the ODEProblem","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"prob = ODEProblem(odefun, [1.0, 0.0, 0.0], (0.0, 1e5), [0.04, 3e7, 1e4])\nsol = solve(prob, Rosenbrock23())","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"The solution can now be indexed symbolically:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"sol[:y₁]","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"sol(1e3, idxs=:y₁)","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"However, we did not give names to the parameters or the independent variables. They can be specified using SymbolCache as well:","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"sys = SymbolCache([:y₁, :y₂, :y₃], [:k₁, :k₂, :k₃], :t)\nodefun = ODEFunction(rober!; sys = sys)\nprob = ODEProblem(odefun, [1.0, 0.0, 0.0], (0.0, 1e5), [0.04, 3e7, 1e4])\nsol = solve(prob, Rosenbrock23())\ngetk1 = getp(sys, :k₁)\n\ngetk1(prob)","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"getk1(sol)","category":"page"},{"location":"simple_sii_sys/","page":"Simple Demonstration of a Symbolic System Structure","title":"Simple Demonstration of a Symbolic System Structure","text":"sol[:t]","category":"page"},{"location":"solution_wrappers/#Defining-Solution-Wrapper-Fallbacks","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"","category":"section"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"The simplest case is when the type contains an object that already implements the interface. All its methods can simply be forwarded to that object. To do so, SymbolicIndexingInterface.jl provides the symbolic_container method. For example,","category":"page"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"struct MySolutionWrapper{T<:SciMLBase.AbstractTimeseriesSolution}\n sol::T\n # other properties...\nend\n\nsymbolic_container(sys::MySolutionWrapper) = sys.sol","category":"page"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"MySolutionWrapper wraps an AbstractTimeseriesSolution which already implements the interface. Since symbolic_container will return the wrapped solution, all method calls such as is_parameter(sys::MySolutionWrapper, sym) will be forwarded to is_parameter(sys.sol, sym).","category":"page"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"In cases where some methods need to function differently than those of the wrapped type, they can be selectively defined. For example, suppose MySolutionWrapper does not support observed quantities. The following method can be defined (in addition to the one above):","category":"page"},{"location":"solution_wrappers/","page":"Defining Solution Wrapper Fallbacks","title":"Defining Solution Wrapper Fallbacks","text":"is_observed(sys::MySolutionWrapper, sym) = false","category":"page"},{"location":"#SymbolicIndexingInterface.jl:-Standardized-Symbolic-Indexing-of-Julia","page":"Home","title":"SymbolicIndexingInterface.jl: Standardized Symbolic Indexing of Julia","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"SymbolicIndexingInterface.jl is a set of interface functions for handling containers of symbolic variables.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To install SymbolicIndexingInterface.jl, use the Julia package manager:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"SymbolicIndexingInterface\")","category":"page"},{"location":"#Introduction","page":"Home","title":"Introduction","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"The symbolic indexing interface has 2 levels:","category":"page"},{"location":"","page":"Home","title":"Home","text":"The user level. At the user level, a modeler or engineer simply uses terms from a domain-specific language (DSL) inside of SciML functionality and will receive the requested values. For example, if a DSL defines a symbol x, then sol[x] returns the solution value(s) for x.\nThe DSL system structure level. This is the structure which defines the symbolic indexing for a given problem/solution. DSLs can tag a constructed problem/solution with this object in order to endow the SciML tools with the ability to index symbolically according to the definitions the DSL writer wants.","category":"page"},{"location":"#Contributing","page":"Home","title":"Contributing","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Please refer to the SciML ColPrac: Contributor's Guide on Collaborative Practices for Community Packages for guidance on PRs, issues, and other matters relating to contributing to SciML.\nThere are a few community forums:\nthe #diffeq-bridged channel in the Julia Slack\nJuliaDiffEq on Gitter\non the Julia Discourse forums\nsee also SciML Community page","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"
The documentation of this SciML package was built using these direct dependencies,","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
and using this machine and Julia version.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
A more complete overview of all dependencies and their versions is also provided.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"using TOML\nusing Markdown\nversion = TOML.parse(read(\"../../Project.toml\", String))[\"version\"]\nname = TOML.parse(read(\"../../Project.toml\", String))[\"name\"]\nlink_manifest = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Manifest.toml\"\nlink_project = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Project.toml\"\nMarkdown.parse(\"\"\"You can also download the\n[manifest]($link_manifest)\nfile and the\n[project]($link_project)\nfile.\n\"\"\")","category":"page"}] } diff --git a/dev/simple_sii_sys/index.html b/dev/simple_sii_sys/index.html index a868799..88443f7 100644 --- a/dev/simple_sii_sys/index.html +++ b/dev/simple_sii_sys/index.html @@ -107,4 +107,4 @@ 64593.73530179436 77241.71691097679 92180.81843146283 - 100000.0 + 100000.0 diff --git a/dev/solution_wrappers/index.html b/dev/solution_wrappers/index.html index b3c39f0..f6822f4 100644 --- a/dev/solution_wrappers/index.html +++ b/dev/solution_wrappers/index.html @@ -8,4 +8,4 @@ # other properties... end -symbolic_container(sys::MySolutionWrapper) = sys.sol

MySolutionWrapper wraps an AbstractTimeseriesSolution which already implements the interface. Since symbolic_container will return the wrapped solution, all method calls such as is_parameter(sys::MySolutionWrapper, sym) will be forwarded to is_parameter(sys.sol, sym).

In cases where some methods need to function differently than those of the wrapped type, they can be selectively defined. For example, suppose MySolutionWrapper does not support observed quantities. The following method can be defined (in addition to the one above):

is_observed(sys::MySolutionWrapper, sym) = false
+symbolic_container(sys::MySolutionWrapper) = sys.sol

MySolutionWrapper wraps an AbstractTimeseriesSolution which already implements the interface. Since symbolic_container will return the wrapped solution, all method calls such as is_parameter(sys::MySolutionWrapper, sym) will be forwarded to is_parameter(sys.sol, sym).

In cases where some methods need to function differently than those of the wrapped type, they can be selectively defined. For example, suppose MySolutionWrapper does not support observed quantities. The following method can be defined (in addition to the one above):

is_observed(sys::MySolutionWrapper, sym) = false
diff --git a/dev/usage/1193213b.svg b/dev/usage/eb2d1a29.svg similarity index 98% rename from dev/usage/1193213b.svg rename to dev/usage/eb2d1a29.svg index 8a9ab97..9f781e0 100644 --- a/dev/usage/1193213b.svg +++ b/dev/usage/eb2d1a29.svg @@ -1,46 +1,46 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/usage/index.html b/dev/usage/index.html index 8a43a21..509e210 100644 --- a/dev/usage/index.html +++ b/dev/usage/index.html @@ -142,7 +142,7 @@ -7.739577070649573 -5.727345621871555
sol(1.3, idxs=[:y, :z])
2-element Vector{Float64}:
  -1.6888746814711415
-  3.7011061302491606
plot(sol, idxs=x)
Example block output

If necessary, Symbols can be used to refer to variables. This is only valid for symbolic variables for which hasname returns true. The Symbol used must match the one returned by getname for the variable.

hasname(x)
true
getname(x)
:x
sol[(:x, :w)]
1495-element Vector{Tuple{Float64, Float64}}:
+  3.7011061302491606
plot(sol, idxs=x)
Example block output

If necessary, Symbols can be used to refer to variables. This is only valid for symbolic variables for which hasname returns true. The Symbol used must match the one returned by getname for the variable.

hasname(x)
true
getname(x)
:x
sol[(:x, :w)]
1495-element Vector{Tuple{Float64, Float64}}:
  (1.0, 1.0)
  (1.0002823510089436, 1.001695702996486)
  (1.0030752466310615, 1.0186439140427952)
@@ -213,4 +213,4 @@
 parameter_values(prob)
3-element Vector{Float64}:
  29.0
  11.0
-  2.5
Note

These getters and setters generate high-performance functions for the specific chosen symbols or collection of symbols. Caching the getter/setter function and reusing it on other problem/solution instances can be the key to achieving good performance. Note that this caching is allowed only when the symbolic system is unchanged (it's fine for the numerical values to have changed, but not the underlying equations).

+ 2.5
Note

These getters and setters generate high-performance functions for the specific chosen symbols or collection of symbols. Caching the getter/setter function and reusing it on other problem/solution instances can be the key to achieving good performance. Note that this caching is allowed only when the symbolic system is unchanged (it's fine for the numerical values to have changed, but not the underlying equations).