-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New generic continuation type and new matcher interface #132
Conversation
@awage @KalelR alright, I think this is some really good work on the generic global continuation method. Have a look at the documentation: Attractors.jl/src/continuation/continuation_ascm_generic.jl Lines 5 to 94 in 41a1fea
@KalelR this method is so general, that now we truly don't need to create a new continuation type "basin enclosure". The only thing we have to do is to add a dispatch to the function: Attractors.jl/src/matching/matching_interface.jl Lines 29 to 38 in 41a1fea
Do you think you can create this function from your original code and PR it? Everything else will just work and you only need to define this function to have basin enclosure. You can have it with or without seeding, and with any mapper, whether it is featurizing or recurrences. I improved the featurizing code to always store the attractors via evolving the trajectories, irrespectively if the sampler is random or pre-described initial condiitons. I also think that your preferred grouping method with "PairwiseComparison" will benefit from the seeding, as the first initial conditions will likely be the attractors, so they will form the center of the clusters for you. |
This is looking very good. The interface is great. |
Thanks @awage . @KalelR I've finished the Basin enclosure matcher, which now is indeed a matcher. I departed significantly from your code in #133 , however, I think the overall logic stays similar. The advantage of my approach is that the source code is significantly smaller as it re-uses existing structures. I decided to use Here is the full source code: """
MatchByBasinEnclosure(; kw...) <: IDMatcher
A matcher that matches attractors by whether they are enclosed in
the basin of a new attractor or not.
## Keyword arguments
- `ε = nothing`: distance threshold given to [`AttractorsViaProximity`](@ref).
If `nothing`, it is estimated as half the minimum distance of centroids
(in contrast to the default more accurate estimation in [`AttractorsViaProximity`](@ref)).
- `Δt = 1, consecutive_lost_steps = 1000`: also given to [`AttractorsViaProximity`](@ref).
Note that attractors that did not convergen anywhere within this number of steps
do not get assigned ID -1 as in [`AttractorsViaProximity`](@ref). Rather, they
get assigned the next available free ID.
- `distance = Centroid()`: metric to estimate distances between state space sets
in case there are co-flowing attractors, see below.
- `seeding = A -> A[end]`: how to select a point from the attractor to see if
it is enclosed in the basin of a new attractor.
## Description
An attractor `A₋` is a set in a state space that occupies a particular region
(or, a single point, if it is a fixed point).
This region is always within the basin of said attractor.
When the parameter of the dynamical system is incremented,
the attractors `A₊` in the new parameter have basins that may have changed in shape and size.
The previous attractor `A₋` is "matched" (i.e., has its ID changed)
to a new attractor `A₊` attractor if `A₋` is located inside the basin of attraction of `A₊`.
To see if `A₋` is in the basin of `A₊`, we first pick a point from `A₊` using the `seeding`
keyword argument. By default this is the last point on the attractor, but it could be anything
else, including the centroid of the attractor (`mean(A)`).
This point is given as an initial condition to an [`AttractorsViaProximity`](@ref) mapper
that maps initial conditions to the `₊` attractors with threshold `ε`
(by default estimated automatically, see [`AttractorsViaProximity`](@ref)).
There can be the situation where multiple `₋` attractors get matched to the same `₊`
attractor, which we call "coflowing attractors". In this scenario matching is prioritized
for the `₋` attractor that is closest to the `₊` in terms of state space set distance,
which is estimated with the `distance` keyword, which can be anything
[`setsofsets_distances`](@ref) accepts. The closes `₋` coflowing attractor
gets assigned the same ID as the `₊` one, while the rest get different unique IDs.
Basin enclosure is a concept similar to "basin instability" in [Ritchie2023](@cite).
"""
@kwdef struct BasinEnclosure{E, D} <: IDMatcher
ε::E = nothing
distance::D = Centroid()
seeding::S = A -> A[end]
Δt::Float64 = 1
consecutive_lost_steps::Int = 1000
end
function _match_attractors(
current_attractors, prev_attractors, matcher::MatchByBasinEnclosure,
mapper, p, pprev
)
ds = referenced_dynamical_system(mapper)
if matcher.ε === nothing
e = ε_from_centroids(attractors)
else
e = matcher.ε
end
proximity = AttractorsViaProximity(ds, current_attractors, e; horizon_limit = Inf)
rmap = Dict(k => proximity(matcher.seeding(A)) for (k, A) in prev_attractors)
# we now process the replacement map `rmap` for co-flowing or diverged attractors.
next_id = next_free_id(current_attractors, prev_attractors)
# take care of diverged attractors
for (old_ID, new_ID) in rmap
if new_ID < 0 # diverged attractors get -1 ID.
rmap[old_ID] = next_id
next_id += 1
end
end
# next, take care of co-flowing attractors
if unique(values(rmap)) != length(rmap) # a value is repeated
# Do coflowing and assign `next_id` to the least distant coflowing
end
return rmap
end
function ε_from_centroids(attractors)
distances = setsofsets_distances(attractors, attractors, Centroid())
alldists = sort!(vcat([collect(values(d)) for (k,d) in distances]...))
filter!(!iszero, alldists)
return minimum(alldists)/2
end The only thing left is to implement the co-flowing logic. EDIT: ah shit, I now realize that there is something wrong here. My logic is opposite. We must create a replacement map mapping CURRENT ids to OLD ids. In the code above I have it the wrong way around, I map OLD ids to CURRENT ids. So at the very end of the function we must "invert" the mapping to return the correct replacement map. |
One thing you didn't explain in #133 is how you handle the "other" attractors in the coflowing case. The closest attractor gets the ID of the new one it flows to. What about the others though? What ID do they get? Do they get the next free available integer? Or do we check if there are some new attractors where no previous attractor has flowed to, and assign this ID by proximity? I think it is easier to assign the next available integer, but we likely will have to test this with your Kuramoto models. For now, I will leave this as it is, and let you play around with this implementation by adjusting what happens inside the "co flowing attractors" Now, I will focus on ensuring the tests pass and merge in this PR and tag a new release. |
Sorry I took so long. |
This PR implements the ideas described in #130. There are still some TODOs left.
UPDATE: the matcher infrasturcture is hidden. After talking with Kalel we realized that this new matcher is actually a whole new continuation and is very simple to implement. This PR has the starting code for it but doesn't export anything new yet.
So this PR now focuses on generalizing the seed and match continuation, as well as renames and re-organization, see CHANGELOG!
This PR also removes the "info extraction" business from the seed and match continuation. This appears completely obsolete, as you can always extract any info you want from the attractors after the continuation, with the same cost as doing it during the continuation.
This PR also further improves the matching code to not unecessarily increment IDs in the mathcing process. That means: if we had previous attractors 1, 2, and current attractors 2 and 3, and attractor 3 matches to attractor 2, then the new attractor 2 won't get ID 4, it will get ID 3 as this is the next available integer.
retract_keys
/docs
filesfractions_curves
tofractions_cont
(for continuation)attractors_info
toattractors_cont
(for continuation)