Skip to content

Commit 7162bd5

Browse files
authored
Correct behavior of default u at trajectory (#226)
* Correct behavior of default u at trajectory * fix test suite (hopefully) * fucking poincare map doesn't work i've spent 4 hours on it already * holy fucking shit i finally fixed it oh my gopd * changelog * finally i truly fixed it fucking hell
1 parent 9a137a1 commit 7162bd5

File tree

6 files changed

+48
-25
lines changed

6 files changed

+48
-25
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
# v3.12.0
2+
3+
- Crucial bugfix of the `trajectory` function. While it is documented that the default value
4+
of `u` is the `current_state(ds)`, the source code was incorrectly using `initial_state(ds)`. This is now fixed and `current_state` is used as was stated. Note that `t0` remains as `initial_time` as this is how it was documented.
5+
6+
- The `initial_state(PoincareMap)` used to return the initial state of the parent system
7+
which was almost never on the Poincare plane. Now it properly returns the first state
8+
on the Poincare plane, which coincides with `current_state(PoincareMap)` right after
9+
constructing the map.
10+
11+
- `PoincareMap` had extra clarifications in the docstring about what's going on.
12+
113
# v3.11.0
214

315
Brand new dynamical system `CoupledSDEs` that represents stochastic differential equations. It also comes with a dedicated documentation page.

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.11.2"
4+
version = "3.12.0"
55

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

src/core/trajectory.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ trajectory (but remember to convert the output of `solve` to a `StateSpaceSet`).
3333
Note: if you mix integer and symbolic indexing be sure to initialize the array
3434
as `Any` so that integers `1, 2, ...` are not converted to symbolic expressions.
3535
"""
36-
function trajectory(ds::DynamicalSystem, T, u0 = initial_state(ds);
36+
function trajectory(ds::DynamicalSystem, T, u0 = current_state(ds);
3737
save_idxs = nothing, t0 = initial_time(ds), kwargs...
3838
)
3939
accessor = svector_access(save_idxs)

src/derived_systems/poincare/poincaremap.jl

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ See also [`StroboscopicMap`](@ref), [`poincaresos`](@ref).
2828
* `direction = -1`: Only crossings with `sign(direction)` are considered to belong to
2929
the surface of section. Negative direction means going from less than ``b``
3030
to greater than ``b``.
31-
* `u0 = nothing`: Specify an initial state.
31+
* `u0 = nothing`: Specify an initial state. If `nothing` it is the
32+
`current_state(ds)`.
3233
* `rootkw = (xrtol = 1e-6, atol = 1e-8)`: A `NamedTuple` of keyword arguments
3334
passed to `find_zero` from [Roots.jl](https://github.com/JuliaMath/Roots.jl).
3435
* `Tmax = 1e3`: The argument `Tmax` exists so that the integrator can terminate instead
@@ -74,11 +75,16 @@ and root finding from Roots.jl, to create a high accuracy estimate of the sectio
7475
4. For the special case of `plane` being a `Tuple{Int, <:Real}`, a special `reinit!` method
7576
is allowed with input state of length `D-1` instead of `D`, i.e., a reduced state already
7677
on the hyperplane that is then converted into the `D` dimensional state.
78+
5. The `initial_state(pmap)` returns the state initial state of the map. This is not `u0`
79+
because `u0` is evolved forwards until it resides on the Poincaré plane.
80+
6. In the `reinit!` function, the `t0` keyword denotes the starting time of the
81+
continuous time dynamical system, as the starting time of the `PoincareMap` is by
82+
definition always 0.
7783
7884
## Example
7985
8086
```julia
81-
using DynamicalSystemsBase
87+
using DynamicalSystemsBase, PredefinedDynamicalSystems
8288
ds = Systems.rikitake(zeros(3); μ = 0.47, α = 1.0)
8389
pmap = poincaremap(ds, (3, 0.0))
8490
step!(pmap)
@@ -102,6 +108,7 @@ mutable struct PoincareMap{I<:ContinuousTimeDynamicalSystem, F, P, R, V} <: Disc
102108
# (i.e., given a D-1 dimensional state, create the full D-dimensional state)
103109
dummy::Vector{Float64}
104110
diffidxs::Vector{Int}
111+
state_initial::V
105112
end
106113
Base.parent(pmap::PoincareMap) = pmap.ds
107114

@@ -122,9 +129,10 @@ function PoincareMap(
122129
diffidxs = _indices_on_poincare_plane(plane, D)
123130
pmap = PoincareMap(
124131
ds, plane_distance, planecrossing, Tmax, rootkw,
125-
v, current_time(ds), 0, dummy, diffidxs
132+
v, current_time(ds), 0, dummy, diffidxs, recursivecopy(v),
126133
)
127-
step!(pmap)
134+
step!(pmap) # this ensures that the state is on the hyperplane
135+
pmap.state_initial = recursivecopy(current_state(pmap))
128136
pmap.t = 0 # first step is 0
129137
return pmap
130138
end
@@ -147,6 +155,7 @@ end
147155
initial_time(pmap::PoincareMap) = 0
148156
current_time(pmap::PoincareMap) = pmap.t
149157
current_state(pmap::PoincareMap) = pmap.state_on_plane
158+
initial_state(pmap::PoincareMap) = pmap.state_initial
150159

151160
"""
152161
current_crossing_time(pmap::PoincareMap) → tcross
@@ -169,8 +178,9 @@ end
169178
SciMLBase.step!(pmap::PoincareMap, n::Int, s = true) = (for _ 1:n; step!(pmap); end; pmap)
170179

171180
function SciMLBase.reinit!(pmap::PoincareMap, u0::AbstractArray = initial_state(pmap);
172-
t0 = initial_time(pmap), p = current_parameters(pmap)
181+
t0 = initial_time(pmap.ds), p = current_parameters(pmap)
173182
)
183+
uc = current_state(pmap)
174184
if length(u0) == dimension(pmap)
175185
u = u0
176186
elseif length(u0) == dimension(pmap) - 1
@@ -179,8 +189,13 @@ function SciMLBase.reinit!(pmap::PoincareMap, u0::AbstractArray = initial_state(
179189
error("Dimension of state for `PoincareMap` reinit is inappropriate.")
180190
end
181191
reinit!(pmap.ds, u; t0, p)
182-
step!(pmap) # always step once to reach the PSOS
183-
pmap.t = 0 # first step is 0
192+
# Check if we have to step the map or not
193+
if u0 == initial_state(pmap)
194+
pmap.state_on_plane = copy(u0)
195+
elseif u0 uc # if we were on current state (like in `trajectory`) then we don't step
196+
step!(pmap) # step once to reach the PSOS
197+
end
198+
pmap.t = 0 # first step is always 0
184199
pmap
185200
end
186201

@@ -223,20 +238,18 @@ function poincare_step!(ds, plane_distance, planecrossing, Tmax, rootkw)
223238
# Otherwise evolve until juuuuuust crossing the plane
224239
tprev = t0
225240
while side < 0
226-
(current_time(ds) - t0) > Tmax && break
241+
(current_time(ds) - t0) > Tmax && return (nothing, nothing)
227242
tprev = current_time(ds)
228243
step!(ds)
229244
side = planecrossing(current_state(ds))
230245
end
231-
while side 0
232-
(current_time(ds) - t0) > Tmax && break
246+
while side 0 # when side becomes negative again we've just crossed the plane!
247+
(current_time(ds) - t0) > Tmax && return (nothing, nothing)
233248
tprev = current_time(ds)
234249
step!(ds)
235250
side = planecrossing(current_state(ds))
236251
end
237-
# we evolved too long and no crossing, return nothing
238-
(current_time(ds) - t0) > Tmax && return (nothing, nothing)
239-
# Else, we're guaranteed to have time window before and after the plane
252+
# We're now guaranteed to have time window before and after the plane
240253
time_window = (tprev, current_time(ds))
241254
tcross = Roots.find_zero(plane_distance, time_window, ROOTS_ALG; rootkw...)
242255
ucross = ds(tcross)

test/poincare.jl

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@ u0 = [
2121
Γ = 0.9
2222
p = [μ, ν, Γ]
2323

24-
gissinger_oop = CoupledODEs(gissinger_rule, u0, p)
25-
gissinger_iip = CoupledODEs(gissinger_rule_iip, recursivecopy(u0), p)
26-
2724
# Define appropriate hyperplane for gissinger system
2825
plane1 = (1, 0.0)
2926
# I want hyperperplane defined by these two points:
@@ -34,7 +31,7 @@ gis_plane(μ) = [cross(Np(μ), Nm(μ))..., 0]
3431
plane2 = gis_plane(μ)
3532

3633
function poincare_tests(ds, pmap, plane)
37-
P, t = trajectory(pmap, 10)
34+
P, t = trajectory(pmap, 10, initial_state(pmap))
3835
reinit!(ds)
3936
P2 = poincaresos(ds, plane, 100; rootkw = (xrtol = 1e-12, atol = 1e-12))
4037
@test length(P) == 11
@@ -54,10 +51,10 @@ end
5451

5552
@testset "IIP=$(IIP) plane=$(P)" for IIP in (false, true), P in (1, 2)
5653
rule = !IIP ? gissinger_rule : gissinger_rule_iip
57-
ds = CoupledODEs(rule, recursivecopy(u0), p)
54+
ds = CoupledODEs(rule, u0, p)
5855
plane = P == 1 ? plane1 : plane2
5956
pmap = PoincareMap(ds, plane; rootkw = (xrtol = 1e-12, atol = 1e-12))
60-
u0pmap = recursivecopy(current_state(pmap))
57+
u0pmap = deepcopy(current_state(pmap))
6158
test_dynamical_system(pmap, u0pmap, p;
6259
idt=true, iip=IIP, test_trajectory = true,
6360
test_init_state_equiv=false, u0init = initial_state(pmap))
@@ -77,7 +74,7 @@ end
7774
u0 = fill(10.0, 3)
7875
p = [10, 28, 8/3]
7976
plane = (1, 0.0)
80-
ds = CoupledODEs(lorenz_rule, recursivecopy(u0), p)
77+
ds = CoupledODEs(lorenz_rule, deepcopy(u0), p)
8178

8279
pmap = PoincareMap(ds, plane; rootkw = (xrtol = 1e-12, atol = 1e-12))
8380
P, t = trajectory(pmap, 10)
@@ -91,6 +88,7 @@ end
9188
end
9289

9390
@testset "poincare of dataset" begin
91+
gissinger_oop = CoupledODEs(gissinger_rule, u0, p)
9492
X, t = trajectory(gissinger_oop, 1000.0)
9593
A = poincaresos(X, plane1)
9694
@test dimension(A) == 3

test/test_system_function.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,10 @@ function test_dynamical_system(ds, u0, p0; idt, iip,
114114
@test Y[1] == X[end]
115115

116116
# obtain only first variable
117-
Z, t = trajectory(ds, 10; save_idxs = [1])
117+
Z, t = trajectory(ds, 10, u0init; save_idxs = [1])
118118
@test length(Z) == 11
119119
@test dimension(Z) == 1
120-
@test Z[1][1] == u0[1]
120+
@test Z[1][1] == u0init[1]
121121
else
122122
reinit!(ds)
123123
@test current_state(ds) == u0
@@ -133,7 +133,7 @@ function test_dynamical_system(ds, u0, p0; idt, iip,
133133
@test t2[1] > t[end]
134134

135135
# obtain only first variable
136-
Z, t = trajectory(ds, 3; save_idxs = [1], Δt = 1)
136+
Z, t = trajectory(ds, 3, u0; save_idxs = [1], Δt = 1)
137137
@test length(Z) == 4
138138
@test dimension(Z) == 1
139139
@test Z[1][1] == u0[1]

0 commit comments

Comments
 (0)