Skip to content

Commit 1d86da1

Browse files
authored
finalize v3 (#164)
* first draft of arbitrary steppable * different multiline printing * print ODE solve stuff * increase default poincare tolerance * pmap improvements * more duffing tests * test refactor * require statespacesets v1 * rename for SSSets v1 * finish source of arbitrary steppable * finish arbitrary steppable as well! * better docstring * overall safety increase * use svector/smatrix instead of sarray
1 parent 799c0dc commit 1d86da1

16 files changed

+198
-33
lines changed

CHANGELOG.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ In addition, be sure that you have version `SimpleDiffEq 0.3.0` or greater (whic
9696

9797
# v1.1
9898

99-
* All functionality related to `neighborhood`, `reconstruct` and `Dataset` has moved to a new package: [`DelayEmbeddings`](https://github.com/JuliaDynamics/DelayEmbeddings.jl). It is reexported by `DynamicalSystemsBase`, which is now (as the name suggests) the basis package for defining `DynamicalSystem`.
99+
* All functionality related to `neighborhood`, `reconstruct` and `StateSpaceSet` has moved to a new package: [`DelayEmbeddings`](https://github.com/JuliaDynamics/DelayEmbeddings.jl). It is reexported by `DynamicalSystemsBase`, which is now (as the name suggests) the basis package for defining `DynamicalSystem`.
100100

101101
# v1.0
102102

@@ -115,7 +115,7 @@ In addition, be sure that you have version `SimpleDiffEq 0.3.0` or greater (whic
115115
## Breaking
116116
* Dropped support of Julia versions < 0.7
117117
* `Reconstruction` does not exist anymore. Instead, `reconstruct` is used in it's
118-
place. The function now also always returns a `Dataset`.
118+
place. The function now also always returns a `StateSpaceSet`.
119119
* In the `reconstruct` function, `D` now stands for the number of temporal neighbors
120120
which is **one less** than the dimensionality of the reconstructed space.
121121
* This change was done because it is more intuitive and more scalable when considering multiple timeseries or spatio temporal timeseries.
@@ -157,7 +157,7 @@ very happy to say that these changes reduced *tremendously* the source code!
157157
* Multi-time Reconstruction
158158
* Multi-dimensional, multi-time Reconstruction
159159
* Methods for estimating Reconstruction parameters are now here.
160-
* Reconstruction from Dataset
160+
* Reconstruction from StateSpaceSet
161161
## Breaking
162162
* Corrected name `helies -> heiles`
163163
* Reconstructions now have field `delay` which is the delay time/times and are
@@ -193,7 +193,7 @@ very happy to say that these changes reduced *tremendously* the source code!
193193
parameters are passed directly into the function!!!
194194

195195
# Non-breaking
196-
* Improved the algorithm that converts a Dataset to a Matrix. It is now not only
196+
* Improved the algorithm that converts a StateSpaceSet to a Matrix. It is now not only
197197
faster, but also more clear!
198198

199199
# v0.4.1
@@ -218,15 +218,15 @@ very happy to say that these changes reduced *tremendously* the source code!
218218
## Non-breaking
219219
* Bugfix of `eltype` of `Reconstruction`.
220220
* Added `circlemap` to `Systems`.
221-
* Bugfix on `Dataset` that incorrect methods were being called due to missing `<:`.
222-
* Method `Base.getindex(d::AbstractDataset{D,T}, ::Colon, j<:AbstractVector{Int}) ` now exists!
221+
* Bugfix on `StateSpaceSet` that incorrect methods were being called due to missing `<:`.
222+
* Method `Base.getindex(d::AbstractStateSpaceSet{D,T}, ::Colon, j<:AbstractVector{Int}) ` now exists!
223223
* Added function `evolve!`.
224224
* Clearly state that we do not support matrices in the equations of motion.
225225
* Bugfixes regarding ODEProblem.
226226

227227
# v0.3.2
228228
## Non-breaking
229-
* Orders of magnitude speed-up in conversions between `Matrix` and `Dataset`,
229+
* Orders of magnitude speed-up in conversions between `Matrix` and `StateSpaceSet`,
230230
because now methods use `transpose` internally and only `reinterpret`.
231231

232232
# v0.3.1

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ OrdinaryDiffEq = "6"
2020
Reexport = "1"
2121
Roots = "1, 2"
2222
SciMLBase = "1.19.5"
23-
StateSpaceSets = "0.1"
23+
StateSpaceSets = "1"
2424
julia = "1.8"

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ set_parameters!
3434
```@docs
3535
step!(::DynamicalSystem, args...; kwargs...)
3636
trajectory
37-
Dataset
37+
StateSpaceSet
3838
```
3939

4040
## `DeterministicIteratedMap`

src/core/dynamicalsystem_interface.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Concrete subtypes typically also contain more information than the above 3 items
4343
4444
In this scope dynamical systems have a known dynamic rule `f` defined as a
4545
standard Julia function. _Observed_ or _measured_ data from a dynamical system
46-
are represented using `AbstractDataset` and are finite.
46+
are represented using `AbstractStateSpaceSet` and are finite.
4747
Such data are obtained from the [`trajectory`](@ref) function or
4848
from an experimental measurement of a dynamical system with an unknown dynamic rule.
4949
@@ -294,15 +294,15 @@ end
294294
# API - step and reset
295295
###########################################################################################
296296
"""
297-
step!(ds::DiscreteTimeDynamicalSystem [, dt::Integer]) → ds
297+
step!(ds::DiscreteTimeDynamicalSystem [, n::Integer]) → ds
298298
299-
Evolve the discrete time dynamical system for 1 or `dt` steps.
299+
Evolve the discrete time dynamical system for 1 or `n` steps.
300300
301301
step!(ds::ContinuousTimeDynamicalSystem, [, dt::Real [, stop_at_tdt]]) → ds
302302
303303
Evolve the continuous time dynamical system for one integration step.
304304
305-
Alternative, if a `dt` is given, then progress the integration until
305+
Alternatively, if a `dt` is given, then progress the integration until
306306
there is a temporal difference `≥ dt` (so, step _at least_ for `dt` time).
307307
308308
When `true` is passed to the optional third argument,

src/core/pretty_printing.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@
66
Base.summary(ds::DynamicalSystem) =
77
"$(dimension(ds))-dimensional $(nameof(typeof(ds)))"
88

9+
function Base.show(io::IO, ds::DynamicalSystem)
10+
print(io, summary(ds))
11+
end
12+
913
# Extend this function to return a vector of `Pair`s of `"description" => value`
1014
additional_details(::DynamicalSystem) = []
1115

12-
function Base.show(io::IO, ds::DynamicalSystem)
16+
function Base.show(io::IO, ::MIME"text/plain", ds::DynamicalSystem)
1317
descriptors = [
1418
"deterministic" => isdeterministic(ds),
1519
"discrete time" => isdiscretetime(ds),

src/core/trajectory.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ after evolving it for total time `T`.
88
`u0` is the state given given to [`reinit!`](@ref) prior to time evolution
99
and defaults to [`initial_state(ds)`](@ref).
1010
11-
See [`Dataset`](@ref) for info on how to use `X`.
11+
See [`StateSpaceSet`](@ref) for info on how to use `X`.
1212
The returned time vector is `t = (t0+Ttr):Δt:(t0+Ttr+T)`.
1313
1414
## Keyword arguments
@@ -44,7 +44,7 @@ function trajectory_discrete(ds, T; Δt::Integer = 1, Ttr::Integer = 0, accessor
4444
step!(ds, Δt)
4545
data[i] = SVector{X, ET}(obtain_access(current_state(ds), accessor))
4646
end
47-
return Dataset(data), tvec
47+
return StateSpaceSet(data), tvec
4848
end
4949

5050
function trajectory_continuous(ds, T; Δt = 0.1, Ttr = 0.0, accessor = nothing)
@@ -61,7 +61,7 @@ function trajectory_continuous(ds, T; Δt = 0.1, Ttr = 0.0, accessor = nothing)
6161
end
6262
sol[i] = SVector{X, ET}(obtain_access(ds(t), accessor))
6363
end
64-
return Dataset(sol), tvec
64+
return StateSpaceSet(sol), tvec
6565
end
6666

6767
# Util functions for `trajectory`
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
export ArbitrarySteppable
2+
3+
"""
4+
ArbitrarySteppable <: DiscreteTimeDynamicalSystem
5+
ArbitrarySteppable(
6+
model, step!, extract_state, extract_parameters, reset_model!;
7+
isdeterministic = true, set_state = reinit!,
8+
)
9+
10+
A dynamical system generated by an arbitrary "model" that can be stepped _in-place_
11+
with some function `step!(model)` for 1 step.
12+
The state of the model is extracted by the `extract_state(model) -> u` function
13+
The parameters of the model are extracted by the `extract_parameters(model) -> p` function.
14+
The system may be re-initialized, via [`reinit!`](@ref), with the `reset_model!`
15+
user-provided function that must have the call signature
16+
```julia
17+
reset_model!(model, u, p)
18+
```
19+
given a (potentially new) state `u` and parameter container `p`, both of which will
20+
default to the initial ones in the [`reinit!`](@ref) call.
21+
22+
`ArbitrarySteppable` exists to provide the DynamicalSystems.jl interface to models from
23+
other packages that could be used within the DynamicalSystems.jl library.
24+
`ArbitrarySteppable` follows the [`DynamicalSystem`](@ref) interface with
25+
the following adjustments:
26+
27+
- [`initial_time`](@ref) is always 0, as time counts the steps the model has
28+
taken since creation or last [`reinit!`](@ref) call.
29+
- [`set_state!`](@ref) is the same as [`reinit!`](@ref) by default.
30+
If not, the keyword argument `set_state` is a function `set_state(model, u)`
31+
that sets the state of the model to `u`.
32+
- The keyword `isdeterministic` should be set properly, as it decides whether
33+
downstream algorithms should error or not.
34+
"""
35+
struct ArbitrarySteppable{M, F, R, ES, EP, U, P, S} <: DiscreteTimeDynamicalSystem
36+
model::M
37+
step::F
38+
reinit::R
39+
extract_state::ES
40+
extract_parameters::EP
41+
t::Base.RefValue{Int}
42+
u0::U
43+
p0::P
44+
isdeterministic::Bool
45+
set_state::S
46+
end
47+
48+
function ArbitrarySteppable(model, step, extract_state, extract_parameters, reinit;
49+
isdeterministic = true, set_state = (ds, u) -> reinit(ds, u, current_parameters(ds)),
50+
)
51+
p0 = deepcopy(extract_parameters(model))
52+
u0 = deepcopy(extract_state(model))
53+
if !(u0 isa AbstractArray{<:Real})
54+
@warn """
55+
The state isn't an `AbstractArray{<:Real}` and that may cause problems
56+
in downstream functions.
57+
"""
58+
end
59+
return ArbitrarySteppable(model, step, reinit, extract_state, extract_parameters,
60+
Ref(0), u0, p0, isdeterministic, set_state)
61+
end
62+
63+
# Extend DynamicalSystems.jl interface
64+
current_state(ds::ArbitrarySteppable) = ds.extract_state(ds.model)
65+
current_parameters(ds::ArbitrarySteppable) = ds.extract_parameters(ds.model)
66+
isdeterministic(ds::ArbitrarySteppable) = ds.isdeterministic
67+
dynamic_rule(ds::ArbitrarySteppable) = ds.step
68+
current_time(ds::ArbitrarySteppable) = ds.t[]
69+
initial_time(ds::ArbitrarySteppable) = 0
70+
SciMLBase.isinplace(ds::ArbitrarySteppable) = true
71+
72+
function SciMLBase.step!(ds::ArbitrarySteppable, n::Int = 1, stop::Bool = true)
73+
for _ in 1:n
74+
ds.step(ds.model)
75+
end
76+
ds.t[] = ds.t[] + n
77+
return ds
78+
end
79+
80+
function SciMLBase.reinit!(ds::ArbitrarySteppable, u = initial_state(ds);
81+
p = current_parameters(ds), t0 = 0, # t0 is not used but required for downstream.
82+
)
83+
isnothing(u) && return
84+
ds.reinit(ds.model, u, p)
85+
ds.t[] = 0
86+
return ds
87+
end
88+
89+
function set_state!(ds::ArbitrarySteppable, u)
90+
ds.set_state(ds.model, u)
91+
return ds
92+
end
93+
94+
additional_details(ds::ArbitrarySteppable) = [
95+
"model type" => nameof(typeof(ds.model)),
96+
]

src/core_systems/continuous_time_ode.jl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ function SciMLBase.ODEProblem(ds::CoupledODEs{IIP}, tspan = (initial_time(ds), I
103103
ODEProblem{IIP}(dynamic_rule(ds), initial_state(ds), tspan, current_parameters(ds))
104104
end
105105

106+
# Pretty print
107+
function additional_details(ds::CoupledODEs)
108+
solver, remaining = _decompose_into_solver_and_remaining(ds.diffeq)
109+
return ["ODE solver" => string(nameof(typeof(solver))),
110+
"ODE kwargs" => remaining,
111+
]
112+
end
113+
106114
###########################################################################################
107115
# Extend interface and extend for `DEIntegrator`
108116
###########################################################################################
@@ -135,7 +143,13 @@ current_time(integ::DEIntegrator) = integ.t
135143
initial_time(integ::DEIntegrator) = integ.sol.prob.tspan[1]
136144

137145
function set_state!(integ::DEIntegrator, u)
138-
integ.u = recursivecopy(u)
146+
if integ.u isa Array{<:Real}
147+
integ.u .= u
148+
elseif integ.u isa Union{SVector, SMatrix}
149+
integ.u = u
150+
else
151+
integ.u = recursivecopy(u)
152+
end
139153
u_modified!(integ, true)
140154
return
141155
end

src/derived_systems/parallel_systems.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,18 @@ initial_states(pdtds::PDTDS) = [initial_state(ds) for ds in pdtds.systems]
182182

183183
# Set stuff
184184
set_parameter!(pdtds::PDTDS) = for ds in pdtds.systems; set_parameter!(ds, args...); end
185-
set_state!(pdtds::PDTDS, u, i::Int = 1) = set_state!(pdtds.systems[i], u)
185+
function set_state!(pdtds::PDTDS, u, i::Int = 1)
186+
# We need to set state in all systems, in case this does
187+
# some kind of resetting, e.g., the `u_modified!` stuff.
188+
for (k, ds) in enumerate(pdtds.systems)
189+
if k == i
190+
set_state!(ds, u)
191+
else
192+
set_state!(ds, current_state(ds))
193+
end
194+
end
195+
end
196+
186197
function SciMLBase.reinit!(pdtds::PDTDS, states = initial_states(pdtds); kwargs...)
187198
for (ds, s) in zip(pdtds.systems, states); reinit!(ds, s; kwargs...); end
188199
end

src/derived_systems/poincare/hyperplane.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,25 +75,25 @@ end
7575
# TODO: Nice improvement would be to use cubic interpolation instead of linear,
7676
# using points i-2, i-1, i, i+1
7777
"""
78-
poincaresos(A::AbstractDataset, plane; kwargs...) → P::Dataset
78+
poincaresos(A::AbstractStateSpaceSet, plane; kwargs...) → P::StateSpaceSet
7979
8080
Calculate the Poincaré surface of section of the given dataset with the given `plane`
8181
by performing linear interpolation betweeen points that sandwich the hyperplane.
8282
8383
Argument `plane` and keywords `direction, warning, save_idxs`
8484
are the same as in the method below.
8585
"""
86-
function poincaresos(A::AbstractDataset, plane;
86+
function poincaresos(A::AbstractStateSpaceSet, plane;
8787
direction = -1, warning = true, save_idxs = dimension(A)
8888
)
8989
check_hyperplane_match(plane, size(A, 2))
9090
i = typeof(save_idxs) <: Int ? save_idxs : SVector{length(save_idxs), Int}(save_idxs...)
9191
planecrossing = PlaneCrossing(plane, direction > 0)
9292
data = poincaresos(A, planecrossing, i)
9393
warning && length(data) == 0 && @warn PSOS_ERROR
94-
return Dataset(data)
94+
return StateSpaceSet(data)
9595
end
96-
function poincaresos(A::Dataset, planecrossing::PlaneCrossing, j)
96+
function poincaresos(A::StateSpaceSet, planecrossing::PlaneCrossing, j)
9797
i, L = 1, length(A)
9898
data = _initialize_output(A[1], j)
9999
# Check if initial condition is already on the plane

0 commit comments

Comments
 (0)