Skip to content

Commit 26bde72

Browse files
authored
Fix of match sequentially with past (#170)
* imrpove comparison with BFKit * allow default option in next id * Revert "allow default option in next id" This reverts commit 3becca5. * critical fix of rematch with past with empty attractors when eg. all initial conditions diverge * bump project * add a todo, I am too tired to do this now... * add test for interrupted attractors with vanished * make sure empty dictionaries are handled in the matching maps * make empty attractors handled ONLY at matching maps * bump changelog
1 parent 4ad613c commit 26bde72

File tree

7 files changed

+53
-19
lines changed

7 files changed

+53
-19
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# v1.24
22

33
- Improved `continuation_series`: now it works for any type of input, including attractor continuation info because the default value (now renamed to fill value) is used to deduce the type of the series vectors.
4+
- Fixed a critical bug where global continuation would fail if there was a parameter where all initial conditions diverge and the matching was with vanished.
45

56
# v1.23
67

Diff for: Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name = "Attractors"
22
uuid = "f3fd9213-ca85-4dba-9dfd-7fc91308fec7"
33
authors = ["George Datseris <[email protected]>", "Kalel Rossi", "Alexandre Wagemakers"]
44
repo = "https://github.com/JuliaDynamics/Attractors.jl.git"
5-
version = "1.24.2"
5+
version = "1.24.3"
66

77

88
[deps]

Diff for: src/dict_utils.jl

+15-8
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,21 @@ end
104104
105105
Return the minimum key of the "new" dictionary
106106
that doesn't exist in the "old" dictionary.
107+
If one of the two dictionaries are empty, return its maximum key + 1.
108+
If both are empty, return 1.
109+
110+
The function assumes tha the dictionary keys are integers.
107111
"""
108-
function next_free_id(a₊::AbstractDict, a₋::AbstractDict)
109-
s = setdiff(keys(a₊), keys(a₋))
110-
nextid = isempty(s) ? maximum(keys(a₋)) + 1 : minimum(s)
111-
return nextid
112-
end
112+
next_free_id(a₊::AbstractDict, a₋::AbstractDict) = next_free_id(keys(a₊), keys(a₋))
113113
function next_free_id(keys₊, keys₋)
114-
s = setdiff(keys₊, keys₋)
115-
nextid = isempty(s) ? maximum(keys₋) + 1 : minimum(s)
116-
return nextid
114+
if length(keys₋) == 0 && length(keys(keys₊)) == 0
115+
return 1
116+
elseif length(keys₋) == 0
117+
return maximum(keys₊) + 1
118+
elseif length(keys₊) == 0
119+
return maximum(keys₋) + 1
120+
else
121+
s = setdiff(keys₊, keys₋)
122+
return isempty(s) ? maximum(keys₋) + 1 : minimum(s)
123+
end
117124
end

Diff for: src/matching/basin_enclosure.jl

+5
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ function matching_map(
5959
current_attractors, prev_attractors, matcher::MatchByBasinEnclosure;
6060
ds, p, pprev = nothing, next_id = next_free_id(current_attractors, prev_attractors)
6161
)
62+
# if either dictionary is empty there is no matching to be done
63+
if isempty(current_attractors) || isempty(prev_attractors)
64+
return Dict{keytype(a₊), keytype(a₋)}()
65+
end
66+
6267
if matcher.ε === nothing
6368
e = ε_from_centroids(current_attractors)
6469
else

Diff for: src/matching/matching_interface.jl

+12-10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ The values contained in `a₊, a₋` can be anything supported by `matcher`.
4444
Within Attractors.jl they are typically `StateSpaceSet`s representing attractors.
4545
Typically the +,- mean after and before some change of parameter of a dynamical system.
4646
47+
`matching_map` always returns an empty dictionary of either `a₊, a₋` is empty.
48+
4749
## Keyword arguments
4850
4951
- `ds`: the dynamical system that generated `a₊, a₋`.
@@ -168,15 +170,14 @@ function _rematch_ignored!(attractors_cont, matcher;
168170
for i in 1:length(attractors_cont)-1
169171
a₊, a₋ = attractors_cont[i+1], attractors_cont[i]
170172
p, pprev = pcurve[i+1], pcurve[i]
171-
# If there are no attractors, skip the matching
172-
if (isempty(a₊) || isempty(a₋))
173-
push!(rmaps, Dict{keytype(attractors_cont[1]), keytype(attractors_cont[1])}())
174-
continue
173+
# If there attractors, update the max id
174+
if !(isempty(a₊) || isempty(a₋))
175+
# we always compute a next id. In this way, if an attractor disappears
176+
# and reappears, it will get a different (incremented) ID as it should!
177+
next_id_a = max(maximum(keys(a₊)), maximum(keys(a₋)))
178+
next_id = max(next_id, next_id_a) + 1
175179
end
176-
# Here we always compute a next id. In this way, if an attractor disappears
177-
# and reappears, it will get a different (incremented) ID as it should!
178-
next_id_a = max(maximum(keys(a₊)), maximum(keys(a₋)))
179-
next_id = max(next_id, next_id_a) + 1
180+
# matching_map returns empty dict if the inputs are empty dicts
180181
rmap = matching_map!(a₊, a₋, matcher; next_id, ds, p, pprev)
181182
push!(rmaps, rmap)
182183
end
@@ -194,11 +195,12 @@ function _rematch_with_past!(attractors_cont, matcher;
194195
rmaps = Dict{keytype(attractors_cont[1]), keytype(attractors_cont[1])}[]
195196
for i in 1:length(attractors_cont)-1
196197
a₊, a₋ = attractors_cont[i+1], attractors_cont[i]
197-
p, pprev = pcurve[i+1], pcurve[i]
198-
# update ghosts
198+
# first update ghosts
199199
for (k, A) in a₋
200200
latest_ghosts[k] = A
201201
end
202+
# and then match
203+
p, pprev = pcurve[i+1], pcurve[i]
202204
rmap = matching_map!(a₊, latest_ghosts, matcher; pprev, p, ds)
203205
push!(rmaps, rmap)
204206
end

Diff for: src/matching/sssdistance.jl

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ _use_vanished(m::MatchBySSSetDistance) = m.use_vanished
5454
function matching_map(a₊::AbstractDict, a₋::AbstractDict, matcher::MatchBySSSetDistance;
5555
kw...
5656
)
57+
# if either dictionary is empty there is no matching to be done
58+
isempty(a₊) || isempty(a₋) && return Dict{keytype(a₊), keytype(a₋)}()
5759
distances = setsofsets_distances(a₊, a₋, matcher.distance)
5860
keys₊, keys₋ = sort.(collect.(keys.((a₊, a₋))))
5961
_matching_map_distances(keys₊, keys₋, distances::Dict, matcher.threshold; kw...)

Diff for: test/continuation/matching_attractors.jl

+17
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,23 @@ end
141141
end
142142
end
143143
end
144+
@testset "no attractors for a step" begin
145+
atts = deepcopy(allatts)
146+
atts[5] = Dict()
147+
match_sequentially!(atts, MatchBySSSetDistance(use_vanished = true))
148+
@test unique_keys(atts) == 1:3
149+
for i in eachindex(jrange)
150+
if iseven(i)
151+
@test sort!(collect(keys(atts[i]))) == 1:2
152+
else
153+
if i == 5
154+
@test isempty(atts[i])
155+
else
156+
@test sort!(collect(keys(atts[i]))) == 1:3
157+
end
158+
end
159+
end
160+
end
144161
end
145162
end
146163

0 commit comments

Comments
 (0)