Skip to content

Commit 818c7ff

Browse files
Improve documentation structure
no hardcore content here, mostly just restructuring
1 parent c90e884 commit 818c7ff

File tree

6 files changed

+153
-87
lines changed

6 files changed

+153
-87
lines changed

docs/pages.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
pages = [
44
"Home" => "index.md",
5-
"Tutorial" => "tutorial.md",
6-
"Usage" => "usage.md",
5+
"Tutorials" => [
6+
"Using the SciML Symbolic Indexing Interface" => "usage.md",
7+
"Simple Demonstration of a Symbolic System Structure" => "simple_sii_sys.md",
8+
"Implementing the Complete Symbolic Indexing Interface" => "implementing_sii_problemsolution.md",
9+
],
10+
"Defining Solution Wrapper Fallbacks" => "solution_wrappers.md",
711
"API" => "api.md",
812
]

docs/src/tutorial.md renamed to docs/src/complete_sii.md

Lines changed: 59 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,76 @@
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}
4913
end
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)
5928
end
6029

61-
function SymbolicIndexingInterface.variable_index(sys::ExampleSolution, sym)
30+
function SymbolicIndexingInterface.variable_index(sys::ExampleSystem, sym)
6231
get(sys.state_index, sym, nothing)
6332
end
6433

65-
function SymbolicIndexingInterface.variable_symbols(sys::ExampleSolution)
34+
function SymbolicIndexingInterface.variable_symbols(sys::ExampleSystem)
6635
collect(keys(sys.state_index))
6736
end
6837

69-
function SymbolicIndexingInterface.is_parameter(sys::ExampleSolution, sym)
38+
function SymbolicIndexingInterface.is_parameter(sys::ExampleSystem, sym)
7039
haskey(sys.parameter_index, sym)
7140
end
7241

73-
function SymbolicIndexingInterface.parameter_index(sys::ExampleSolution, sym)
42+
function SymbolicIndexingInterface.parameter_index(sys::ExampleSystem, sym)
7443
get(sys.parameter_index, sym, nothing)
7544
end
7645

77-
function SymbolicIndexingInterface.parameter_symbols(sys::ExampleSolution)
46+
function SymbolicIndexingInterface.parameter_symbols(sys::ExampleSystem)
7847
collect(keys(sys.parameter_index))
7948
end
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
8554
end
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]
8958
end
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
11162
end
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
)
12071
end
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)
12879
end
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+
131109
Note that the method definitions are all assuming `constant_structure(p) == true`.
132110

133111
In 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
157135
them is not necessary.
158136

159137
```julia
160-
function SymbolicIndexingInterface.parameter_values(sys::ExampleSolution)
138+
function SymbolicIndexingInterface.parameter_values(sys::ExampleSystem)
161139
sys.p
162140
end
163141
```
164142

165-
# Implementing the `SymbolicTypeTrait` for a type
143+
## Implementing the `SymbolicTypeTrait` for a type
166144

167145
The `SymbolicTypeTrait` is used to identify values that can act as symbolic variables. It
168146
has 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

docs/src/index.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# SymbolicIndexingInterface.jl: Arrays of Arrays and Even Deeper
1+
# SymbolicIndexingInterface.jl: Standardized Symbolic Indexing of Julia
22

33
SymbolicIndexingInterface.jl is a set of interface functions for handling containers
44
of symbolic variables.
@@ -12,6 +12,19 @@ using Pkg
1212
Pkg.add("SymbolicIndexingInterface")
1313
```
1414

15+
## Introduction
16+
17+
The symbolic indexing interface has 2 levels:
18+
19+
1. The user level. At the user level, a modeler or engineer simply uses terms from a
20+
domain-specific language (DSL) inside of SciML functionality and will receive the requested
21+
values. For example, if a DSL defines a symbol `x`, then `sol[x]` returns the solution
22+
value(s) for `x`.
23+
2. The DSL system structure level. This is the structure which defines the symbolic indexing
24+
for a given problem/solution. DSLs can tag a constructed problem/solution with this
25+
object in order to endow the SciML tools with the ability to index symbolically according
26+
to the definitions the DSL writer wants.
27+
1528
## Contributing
1629

1730
- Please refer to the
@@ -79,4 +92,4 @@ file and the
7992
[project]($link_project)
8093
file.
8194
""")
82-
```
95+
```

docs/src/simple_sii_sys.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Simple Demonstration of a Symbolic System Structure
2+
3+
In this tutorial we will show how to implement a system structure type for defining the
4+
symbolic indexing of a domain-specific language. This tutorial will show how the
5+
`SymbolCache` type is defined to take in arrays of symbols for its independent, dependent,
6+
and parameter variable names and uses that to define the symbolic indexing interface.

docs/src/solution_wrappers.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Defining Solution Wrapper Fallbacks
2+
3+
The simplest case is when the type contains an object that already implements the interface.
4+
All its methods can simply be forwarded to that object. To do so, SymbolicIndexingInterface.jl
5+
provides the [`symbolic_container`](@ref) method. For example,
6+
7+
```julia
8+
struct MySolutionWrapper{T<:SciMLBase.AbstractTimeseriesSolution}
9+
sol::T
10+
# other properties...
11+
end
12+
13+
symbolic_container(sys::MySolutionWrapper) = sys.sol
14+
```
15+
16+
`MySolutionWrapper` wraps an `AbstractTimeseriesSolution` which already implements the interface.
17+
Since `symbolic_container` will return the wrapped solution, all method calls such as
18+
`is_parameter(sys::MySolutionWrapper, sym)` will be forwarded to `is_parameter(sys.sol, sym)`.
19+
20+
In cases where some methods need to function differently than those of the wrapped type, they can be selectively
21+
defined. For example, suppose `MySolutionWrapper` does not support observed quantities. The following
22+
method can be defined (in addition to the one above):
23+
24+
```julia
25+
is_observed(sys::MySolutionWrapper, sym) = false
26+
```

0 commit comments

Comments
 (0)