Skip to content

Commit 9546170

Browse files
authored
Symboli indices in trajectory (#200)
* allow arbitrary observe indices in continuous time trajectory * allow using correct time in observe state * allow using any observe index in trajectory * bump version
1 parent ffb470b commit 9546170

File tree

4 files changed

+36
-15
lines changed

4 files changed

+36
-15
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# v3.7.0
2+
3+
Symbolic indices used in `observe_state` can now also be used in `trajectory`.
4+
5+
It is possible to specify time to observe a state at now.
6+
17
# v3.6.0
28

39
New convenience functions `state_name, parameter_name` for obtaining a `name::String`
@@ -15,7 +21,7 @@ The referenced MTK model corresponding to the dynamical system can be obtained w
1521

1622
See also the online overarching tutorial for an example.
1723

18-
At the moment this is only possible for `CoupledODEs` and its derivative systems, as these are the only systems that can be made by a `DEProblem`, however it should be easy to allow e.g., `DeterministicIterated` to be created by a `DiscreteProblem`.
24+
At the moment this is only possible for `CoupledODEs` and its derivative systems, as these are the only systems that can be made by a `DEProblem`, however it should be easy to allow e.g., `DeterministicIteratedMap` to be created by a `DiscreteProblem`.
1925

2026
Also:
2127

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DynamicalSystemsBase"
22
uuid = "6e36e845-645a-534a-86f2-f5d4aa5a06b4"
33
repo = "https://github.com/JuliaDynamics/DynamicalSystemsBase.jl.git"
4-
version = "3.6.0"
4+
version = "3.7.0"
55

66
[deps]
77
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"

src/core/dynamicalsystem_interface.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,35 +172,36 @@ See also [`initial_state`](@ref), [`observe_state`](@ref).
172172
current_state(ds::DynamicalSystem) = ds.u
173173

174174
"""
175-
observe_state(ds::DynamicalSystem, i [,u = current_state(ds)]) → x::Real
175+
observe_state(ds::DynamicalSystem, i, u = current_state(ds)) → x::Real
176176
177177
Return the state `u` of `ds` _observed_ at "index" `i`. Possibilities are:
178178
179179
- `i::Int` returns the `i`-th dynamic variable.
180-
- `i::Function` returns `f(current_state(ds))`, which is asserted to be a real number.
180+
- `i::Function` returns `f(current_state(ds))`.
181181
- `i::SymbolLike` returns the value of the corresponding symbolic variable.
182182
This is valid only for dynamical systems referrencing a ModelingToolkit.jl model
183183
which also has `i` as one of its listed variables (either uknowns or observed).
184184
Here `i` can be anything can be anything
185185
that could index the solution object `sol = ModelingToolkit.solve(...)`,
186186
such as a `Num` or `Symbol` instance with the name of the symbolic variable.
187+
In this case, a last fourth optional positional argument `t` defaults to
188+
`current_time(ds)` and is the time to observe the state at.
187189
188190
For [`ProjectedDynamicalSystem`](@ref), this function assumes that the
189191
state of the system is the full state space state, not the projected one
190192
(this makes the most sense for allowing MTK-based indexing).
191193
192194
Use [`state_name`](@ref) for an accompanying name.
193195
"""
194-
function observe_state(ds::DynamicalSystem, index, u::AbstractArray = current_state(ds))
196+
function observe_state(ds::DynamicalSystem, index, u::AbstractArray = current_state(ds), t = current_time(ds))
195197
if index isa Function
196198
return index(u)
197-
elseif index isa Int
199+
elseif index isa Integer
198200
return u[index]
199201
elseif has_referrenced_model(ds)
200202
prob = referrenced_sciml_prob(ds)
201203
ugetter = SymbolicIndexingInterface.observed(prob, index)
202204
p = current_parameters(ds)
203-
t = current_time(ds)
204205
return ugetter(u, p, t)
205206
else
206207
throw(ArgumentError("Invalid index to observe state, or if symbolic index, the "*

src/core/trajectory.jl

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ to the last valid point.
1919
have access to unicode, the keyword `Dt` can be used instead.
2020
* `Ttr = 0`: Transient time to evolve the initial state before starting saving states.
2121
* `t0 = initial_time(ds)`: Starting time.
22-
* `save_idxs::AbstractVector{Int}`: Which variables to output in `X` (by default all).
22+
* `save_idxs::AbstractVector`: Which variables to output in `X`. It can be
23+
any type of index that can be given to [`observe_state`](@ref).
24+
Defaults to `1:dimension(ds)` (all dynamic variables).
2325
"""
2426
function trajectory(ds::DynamicalSystem, T, u0 = initial_state(ds);
2527
save_idxs = nothing, t0 = initial_time(ds), kwargs...
@@ -43,10 +45,10 @@ function trajectory_discrete(ds, T;
4345
X = isnothing(accessor) ? dimension(ds) : length(accessor)
4446
data = Vector{SVector{X, ET}}(undef, L)
4547
Ttr 0 && step!(ds, Ttr)
46-
data[1] = obtain_access(current_state(ds), accessor)
48+
data[1] = obtain_state(current_state(ds), accessor)
4749
for i in 2:L
4850
step!(ds, Δt)
49-
data[i] = SVector{X, ET}(obtain_access(current_state(ds), accessor))
51+
data[i] = SVector{X, ET}(obtain_state(ds, current_state(ds), accessor))
5052
if !successful_step(ds)
5153
# Diverged trajectory; set final state to remaining set
5254
# and exit iteration early
@@ -72,7 +74,7 @@ function trajectory_continuous(ds, T; Dt = 0.1, Δt = Dt, Ttr = 0.0, accessor =
7274
step!(ds)
7375
successful_step(ds) || break
7476
end
75-
sol[i] = SVector{X, ET}(obtain_access(ds(t), accessor))
77+
sol[i] = SVector{X, ET}(obtain_state(ds, t, accessor))
7678
if !successful_step(ds)
7779
# Diverged trajectory; set final state to remaining set
7880
# and exit iteration early
@@ -86,9 +88,21 @@ function trajectory_continuous(ds, T; Dt = 0.1, Δt = Dt, Ttr = 0.0, accessor =
8688
return StateSpaceSet(sol), tvec
8789
end
8890

89-
# Util functions for `trajectory`
91+
# Util functions for `trajectory` to make indexing static vectors
92+
# as efficient as possible
9093
svector_access(::Nothing) = nothing
91-
svector_access(x::AbstractArray) = SVector{length(x), Int}(x...)
94+
svector_access(x::AbstractVector{Int}) = SVector{length(x), Int}(x...)
9295
svector_access(x::Int) = SVector{1, Int}(x)
93-
obtain_access(u, ::Nothing) = u
94-
obtain_access(u, i::SVector) = u[i]
96+
svector_access(x::AbstractVector) = x
97+
98+
# this is a special wrapper around `observe_state` to make indexing
99+
# static arrays more performant without unecessary allocations
100+
obtain_state(u, ::Nothing) = u
101+
obtain_state(u, i::SVector) = u[i]
102+
function obtain_state(ds::DynamicalSystem, t::Real, i::Union{Nothing, SVector})
103+
return obtain_state(ds(t), i)
104+
end
105+
function obtain_state(ds::DynamicalSystem, t::Real, idxs::AbstractVector)
106+
u = ds(t)
107+
return [observe_state(ds, i, u, t) for i in idxs]
108+
end

0 commit comments

Comments
 (0)