1- # Implementing SymbolicIndexingInterface for a type
1+ # Implementing the Complete Symbolic Indexing Interface
22
3- Implementing the interface for a type allows it to be used by existing symbolic indexing
4- infrastructure. There are multiple ways to implement it, and the entire interface is
5- not always necessary.
6-
7- ## Defining a fallback
8-
9- The simplest case is when the type contains an object that already implements the interface.
10- All its methods can simply be forwarded to that object. To do so, SymbolicIndexingInterface.jl
11- provides the [ ` symbolic_container ` ] ( @ref ) method. For example,
3+ This tutorial will show how to define the entire Symbolic Indexing Interface on an
4+ ` ExampleSystem ` :
125
136``` julia
14- struct MySolutionWrapper{T<: SciMLBase.AbstractTimeseriesSolution }
15- sol:: T
16- # other properties...
17- end
18-
19- symbolic_container (sys:: MySolutionWrapper ) = sys. sol
20- ```
21-
22- ` MySolutionWrapper ` wraps an ` AbstractTimeseriesSolution ` which already implements the interface.
23- Since ` symbolic_container ` will return the wrapped solution, all method calls such as
24- ` is_parameter(sys::MySolutionWrapper, sym) ` will be forwarded to ` is_parameter(sys.sol, sym) ` .
25-
26- In cases where some methods need to function differently than those of the wrapped type, they can be selectively
27- defined. For example, suppose ` MySolutionWrapper ` does not support observed quantities. The following
28- method can be defined (in addition to the one above):
29-
30- ``` julia
31- is_observed (sys:: MySolutionWrapper , sym) = false
32- ```
33-
34- ## Defining the interface in its entirety
35-
36- Not all the methods in the interface are required. Some only need to be implemented if a type
37- supports specific functionality. Consider the following struct, which needs to implement the interface:
38-
39- ``` julia
40- struct ExampleSolution
7+ struct ExampleSystem
418 state_index:: Dict{Symbol,Int}
429 parameter_index:: Dict{Symbol,Int}
4310 independent_variable:: Union{Symbol,Nothing}
4411 # mapping from observed variable to Expr to calculate its value
4512 observed:: Dict{Symbol,Expr}
46- u:: Vector{Vector{Float64}}
47- p:: Vector{Float64}
48- t:: Vector{Float64}
4913end
5014```
5115
16+ Not all the methods in the interface are required. Some only need to be implemented if a type
17+ supports specific functionality. Consider the following struct, which needs to implement the interface:
18+
19+ ## Mandatory methods
5220
21+ ### Simple Indexing Functions
5322
54- ### Mandatory methods
23+ These are the simple functions which describe how to turn symbols into indices.
5524
5625``` julia
57- function SymbolicIndexingInterface. is_variable (sys:: ExampleSolution , sym)
26+ function SymbolicIndexingInterface. is_variable (sys:: ExampleSystem , sym)
5827 haskey (sys. state_index, sym)
5928end
6029
61- function SymbolicIndexingInterface. variable_index (sys:: ExampleSolution , sym)
30+ function SymbolicIndexingInterface. variable_index (sys:: ExampleSystem , sym)
6231 get (sys. state_index, sym, nothing )
6332end
6433
65- function SymbolicIndexingInterface. variable_symbols (sys:: ExampleSolution )
34+ function SymbolicIndexingInterface. variable_symbols (sys:: ExampleSystem )
6635 collect (keys (sys. state_index))
6736end
6837
69- function SymbolicIndexingInterface. is_parameter (sys:: ExampleSolution , sym)
38+ function SymbolicIndexingInterface. is_parameter (sys:: ExampleSystem , sym)
7039 haskey (sys. parameter_index, sym)
7140end
7241
73- function SymbolicIndexingInterface. parameter_index (sys:: ExampleSolution , sym)
42+ function SymbolicIndexingInterface. parameter_index (sys:: ExampleSystem , sym)
7443 get (sys. parameter_index, sym, nothing )
7544end
7645
77- function SymbolicIndexingInterface. parameter_symbols (sys:: ExampleSolution )
46+ function SymbolicIndexingInterface. parameter_symbols (sys:: ExampleSystem )
7847 collect (keys (sys. parameter_index))
7948end
8049
81- function SymbolicIndexingInterface. is_independent_variable (sys:: ExampleSolution , sym)
50+ function SymbolicIndexingInterface. is_independent_variable (sys:: ExampleSystem , sym)
8251 # note we have to check separately for `nothing`, otherwise
8352 # `is_independent_variable(p, nothing)` would return `true`.
8453 sys. independent_variable != = nothing && sym === sys. independent_variable
8554end
8655
87- function SymbolicIndexingInterface. independent_variable_symbols (sys:: ExampleSolution )
56+ function SymbolicIndexingInterface. independent_variable_symbols (sys:: ExampleSystem )
8857 sys. independent_variable === nothing ? [] : [sys. independent_variable]
8958end
9059
91- # this type accepts `Expr` for observed expressions involving state/parameter/observed
92- # variables
93- SymbolicIndexingInterface. is_observed (sys:: ExampleSolution , sym) = sym isa Expr || sym isa Symbol && haskey (sys. observed, sym)
94-
95- function SymbolicIndexingInterface. observed (sys:: ExampleSolution , sym:: Expr )
96- if is_time_dependent (sys)
97- return function (u, p, t)
98- # compute value from `sym`, leveraging `variable_index` and
99- # `parameter_index` to turn symbols into indices
100- end
101- else
102- return function (u, p)
103- # compute value from `sym`, leveraging `variable_index` and
104- # `parameter_index` to turn symbols into indices
105- end
106- end
107- end
108-
109- function SymbolicIndexingInterface. is_time_dependent (sys:: ExampleSolution )
60+ function SymbolicIndexingInterface. is_time_dependent (sys:: ExampleSystem )
11061 sys. independent_variable != = nothing
11162end
11263
113- SymbolicIndexingInterface. constant_structure (:: ExampleSolution ) = true
64+ SymbolicIndexingInterface. constant_structure (:: ExampleSystem ) = true
11465
115- function SymbolicIndexingInterface. all_solvable_symbols (sys:: ExampleSolution )
66+ function SymbolicIndexingInterface. all_solvable_symbols (sys:: ExampleSystem )
11667 return vcat (
11768 collect (keys (sys. state_index)),
11869 collect (keys (sys. observed)),
11970 )
12071end
12172
122- function SymbolicIndexingInterface. all_symbols (sys:: ExampleSolution )
73+ function SymbolicIndexingInterface. all_symbols (sys:: ExampleSystem )
12374 return vcat (
12475 all_solvable_symbols (sys),
12576 collect (keys (sys. parameter_index)),
@@ -128,18 +79,45 @@ function SymbolicIndexingInterface.all_symbols(sys::ExampleSolution)
12879end
12980```
13081
82+ ### Observed Equation Handling
83+
84+ These are for handling symbolic expressions and generating equations which are not directly
85+ in the solution vector.
86+
87+ ``` julia
88+ # this type accepts `Expr` for observed expressions involving state/parameter/observed
89+ # variables
90+ SymbolicIndexingInterface. is_observed (sys:: ExampleSystem , sym) = sym isa Expr || sym isa Symbol && haskey (sys. observed, sym)
91+
92+ function SymbolicIndexingInterface. observed (sys:: ExampleSystem , sym:: Expr )
93+ if is_time_dependent (sys)
94+ return function (u, p, t)
95+ # compute value from `sym`, leveraging `variable_index` and
96+ # `parameter_index` to turn symbols into indices
97+ end
98+ else
99+ return function (u, p)
100+ # compute value from `sym`, leveraging `variable_index` and
101+ # `parameter_index` to turn symbols into indices
102+ end
103+ end
104+ end
105+ ```
106+
107+ ### Note about constant structure
108+
131109Note that the method definitions are all assuming ` constant_structure(p) == true ` .
132110
133111In case ` constant_structure(p) == false ` , the following methods would change:
134- - ` constant_structure(::ExampleSolution ) = false `
135- - ` variable_index(sys::ExampleSolution , sym) ` would become
136- ` variable_index(sys::ExampleSolution , sym i) ` where ` i ` is the time index at which
112+ - ` constant_structure(::ExampleSystem ) = false `
113+ - ` variable_index(sys::ExampleSystem , sym) ` would become
114+ ` variable_index(sys::ExampleSystem , sym i) ` where ` i ` is the time index at which
137115 the index of ` sym ` is required.
138- - ` variable_symbols(sys::ExampleSolution ) ` would become
139- ` variable_symbols(sys::ExampleSolution , i) ` where ` i ` is the time index at which
116+ - ` variable_symbols(sys::ExampleSystem ) ` would become
117+ ` variable_symbols(sys::ExampleSystem , i) ` where ` i ` is the time index at which
140118 the variable symbols are required.
141- - ` observed(sys::ExampleSolution , sym) ` would become
142- ` observed(sys::ExampleSolution , sym, i) ` where ` i ` is either the time index at which
119+ - ` observed(sys::ExampleSystem , sym) ` would become
120+ ` observed(sys::ExampleSystem , sym, i) ` where ` i ` is either the time index at which
143121 the index of ` sym ` is required or a ` Vector ` of state symbols at the current time index.
144122
145123## Optional methods
@@ -157,15 +135,16 @@ the default implementations for `getp` and `setp` will suffice, and manually def
157135them is not necessary.
158136
159137``` julia
160- function SymbolicIndexingInterface. parameter_values (sys:: ExampleSolution )
138+ function SymbolicIndexingInterface. parameter_values (sys:: ExampleSystem )
161139 sys. p
162140end
163141```
164142
165- # Implementing the ` SymbolicTypeTrait ` for a type
143+ ## Implementing the ` SymbolicTypeTrait ` for a type
166144
167145The ` SymbolicTypeTrait ` is used to identify values that can act as symbolic variables. It
168146has three variants:
147+
169148- [ ` NotSymbolic ` ] ( @ref ) for quantities that are not symbolic. This is the default for all
170149 types.
171150- [ ` ScalarSymbolic ` ] ( @ref ) for quantities that are symbolic, and represent a single
0 commit comments