Global continuation
Attractors.global_continuation — Function
global_continuation(gca::GlobalContinuationAlgorithm, pcurve, icsampler; kwargs...)Find and continue attractors (or representations of attractors) and properties of their basins across a parameter curve pcurve according to algorithm gca and by sampling initial conditions using icsampler.
Possible subtypes of a GlobalContinuationAlgorithm are:
pcurve is a Vector of dictionaries, each dictionary mapping parameter indices to values. This defines an arbitrary curve in the parameter space of the dynamical system.
icsampler is a subtype of InitialConditionsSampler and provides instructions for how to sample initial conditions to explore the state space during the continuation.
Return an instance of GlobalContinuationOutput that contains the continued attractors, their basins fractions, and any other additional information.
There is no difference between single or multi parameter global continuation. Use hilbert_pcurve to cover multiparameter spaces.
Keyword arguments
show_progress = true: display a progress bar of the computation.
Description
global_continuation is the central function for performing global continuation as outlined in our article (Datseris et al., 2026).
The global continuation algorithm typically references a BasinMap which is used to find basins and corresponding attractors of a dynamical system. Additional arguments that control how to continue/track/match attractors across a parameter range are given when creating gca.
The basin properties and the attractors (or some representation of them) are continued across the parameter curve, whose elements are given to DynamicalSystems.set_parameters! to update system parameters. Thus, global continuation operates on a prescribed parameter curve.
Attractors.GlobalContinuationAlgorithm — Type
GlobalContinuationAlgorithmSupertype of all algorithms used in global_continuation. Each algorithm typically references a BasinMap, as well as contains more information for how to continue/track/match attractors across a parameter range.
See global_continuation for more.
Attractors.continuation_series — Function
continuation_series(continuation_info, fillval = NaN)Transform a continuation quantity (a vector of dictionaries, each dictionary mapping basin IDs to values of same type as fillval) to a dictionary of vectors where the k dictionary entry is the series of the continuation quantity corresponding to basin with ID k. fillval denotes the value to assign in the series if the basin with ID k does not exist at this particular series index. If the continuation_info is the attractors themselves, you likely want to use as fillval some empty state space set such as StateSpaceSet{D}(), or just the missing value (in which case the output will be of Union type).
This function is typically used on the output of global_continuation.
General seeding-based continuation (ASCM)
Attractors.AttractorSeedContinueMatch — Type
AttractorSeedContinueMatch(bmap, matcher = MatchBySSSetDistance(); seeding)A global continuation method for global_continuation. bmap is any subtype of BasinMap which implements extract_attractors, i.e., it finds the actual attractors. matcher is a configuration of how to match attractor IDs, see IDMatcher for more options.
Description
This is a general and composable global continuation method outlined in our article (Datseris et al., 2026) and based on a 4-step process:
- Seed initial conditions from previously found attractors
- Propagate those forwards to "continue" previous attractors
- Estimate basin fractions and potentially find new attractors
- Match attractors
Step 0 - Finding initial attractors
At the first parameter slice of the global continuation process, attractors and their fractions are found using the given bmap and basins_fractions. See the bmap documentation and BasinMap for details on how this works. Then, from the second parameter onwards the continuation occurs.
Step 1 - Seeding initial conditions
Initial conditions can be seeded from previously found attractors. This is controlled by the seeding keyword, which must be a function that given a StateSpaceSet (an attractor), it returns an iterator of initial conditions. By default the first point of an attractor is provided as the only seed.
Seeding can be turned off by providing the dummy function seeding = A -> [], i.e., it always returns an empty iterator and hence no seeds and we skip to step 2.
Step 2 - Continuing the seeds
The dynamical system referenced by the bmap is now set to the new parameter value. The seeds are run through the bmap to converge to attractors at the new parameter value. Seeding initial conditions close to previous attractors increases the probability that if an attractor continues to exist in the new parameter, it is found. Additionally, for some mappers this seeding process improves the accuracy as well as performance of finding attractors, see e.g. discussion in (Datseris et al., 2023).
This seeding works for any bmap, regardless of if they can map individual initial conditions with the bmap(u0) syntax! If this syntax isn't supported, steps 2 and 3 are done together.
Step 3 - Estimate basins fractions
After the special seeded initial conditions are mapped to attractors, attractor basin fractions are computed by sampling additional initial conditions using the provided ics in global_continuation. I.e., exactly as in basins_fractions. Naturally, during this step new attractors may be found, besides those found using the "seeding from previous attractors".
Step 4 - Matching
Normally the ID an attractor gets assigned is somewhat a random integer. Therefore, to ensure a logical output of the global continuation process, attractors need to be "matched". This means: attractor and fractions must have their IDs changed, so that attractors that are "similar" to those at a previous parameter get assigned the same ID.
What is "similar enough" is controlled by the matcher input. The default matcherMatchBySSSetDistance matches sets which have small distance in state space. The matching algorithm itself can be quite involved, so read the documentation of the matcher for how matching works.
A note on matching: the MatchBySSSetDistance can also be used after the continuation is completed, as it only requires as input the state space sets (attractors), without caring at which parameter each attractor exists at. If you don't like the final matching output, you may use a different instance of MatchBySSSetDistance and call match_sequentially! again on the output, without having to recompute the whole global continuation!
Step 5 - Finish
After matching the parameter(s) is incremented. Steps 1-4 repeat until all parameter values are exhausted.
Further note
This global continuation method is a generalization of the "RAFM" continuation described in (Datseris et al., 2023). This continuation method is still exported as RecurrencesFindAndMatch.
Recurrences continuation
Attractors.RecurrencesFindAndMatch — Function
RecurrencesFindAndMatch <: GlobalContinuationAlgorithm
RecurrencesFindAndMatch(bmap::BasinMapRecurrences; kwargs...)A method for global_continuation as in (Datseris et al., 2023) that is based on the recurrences algorithm for finding attractors (BasinMapRecurrences) and then matching them according to their state space distance.
Keyword arguments
distance = Centroid(), threshold = Inf: passed toMatchBySSSetDistance.seeds_from_attractor: A function that takes as an input an attractor and returns an iterator of initial conditions to be seeded from the attractor for the next parameter slice. By default, we sample only the first stored point on the attractor.
Description
RecurrencesFindAndMatch is a wrapper type. It is has been generalized by AttractorSeedContinueMatch. It is still exported for backwards compatibility and to have a clear reference to the original algorithm developed in (Datseris et al., 2023).
The source code of RecurrencesFindAndMatch is trival: it takes the given bmap, it initializes a MatchBySSSetDistance, and along with seeds_from_attractor it makes the AttractorSeedContinueMatch instance. This is the process described in (Datseris et al., 2023), whereby attractors are found using the recurrences algorithm BasinMapRecurrences and they are then matched by their distance in state space MatchBySSSetDistance.
Grouping continuation (FGAP)
Attractors.FeaturizeGroupAcrossParameter — Type
FeaturizeGroupAcrossParameter <: GlobalContinuationAlgorithm
FeaturizeGroupAcrossParameter(bmap::BasinMapFeaturizeGroup [, info_extraction]; kw...)A method for global_continuation that featurizes and groups trajectories across the whole parameter axis to establish a continuation of the groups.
info_extraction::Function is a function that takes as an input a vector of feature-vectors (corresponding to a cluster) and returns a description of the cluster. By default, the centroid of the cluster is used as its description. This output becomes the attractors representation in GlobalContinuationOutput.
If the keyword store_features is true (default), then this method adds some information into the other field of GlobalContinuationOutput:
"features"contains the features per parameter step"labels"contains their corresponding labels (both containers are sorted)
which allows subsequent analysis of the grouped features.
Description
It uses the featurizing approach discussed in BasinMapFeaturizeGroup and hence requires an instance of that bmap as an input. When used in global_continuation, features are extracted and then grouped across a parameter range. Said differently, all features of all initial conditions across all parameter values are put into the same "pool" and then grouped as dictated by the group_config of the bmap. After the grouping is finished the feature label fractions are distributed to each parameter value they came from.
This continuation method is based on, but strongly generalizes, the approaches in the papers (Gelbrecht et al., 2020) and (Stender and Hoffmann, 2021).
Aggregating attractors
Attractors.aggregate_attractors — Function
aggregate_attractors(attractors::Dict, featurizer, group_config) → agg_attractors, centroids, membersAggregate the attractors of a single parameter (a dictionary mapping IDs to StateSpaceSets, as returned by basins_fractions or one slice of a global_continuation output) by merging similar ones into groups.
This is the single-parameter building block of aggregate_continuation; use it directly when you only want to group the attractors found at one parameter value and there is nothing to continue.
Each attractor is turned into a feature vector with featurizer and the attractors are partitioned into groups with group_config (a GroupingConfig). The members of a group are merged into a single StateSpaceSet.
Return
agg_attractors: likeattractors, but mapping each group ID to the mergedStateSpaceSetof its members.centroids: a dictionary mapping each group ID to the centroid (mean) of its members' feature vectors. This summarises where each group sits in feature space; the per-step version of this is the second output ofaggregate_continuation.members: a dictionary mapping each group ID to the vector of original attractor IDs (the keys ofattractors) that were merged into it.
To compute stability quantifiers for the merged groups, pass agg_attractors to stability_quantifiers_along_continuation (wrapping it and the parameter in length-1 vectors if you have a single parameter).
Attractors.aggregate_continuation — Function
aggregate_continuation(attractors_cont, featurizer, group_config; kw...)Aggregate the attractors of a global_continuation result.
This is the post-processing companion of global_continuation. After a global_continuation with AttractorSeedContinueMatch, call this function to "group" attractors that have similar features into a single merged attractor. The merged attractors carry IDs that stay consistent along the parameter curve.
Arguments
attractors_cont: a vector of dictionaries mapping IDs to attractors (StateSpaceSets), exactly as returned byglobal_continuation(orbasins_fractions).featurizer: a 1-argument function mapping an attractor to a feature vector. Features expected byGroupingConfigare typicallySVectors.group_config: a subtype ofGroupingConfig.
All keyword arguments are propagated into MatchBySSSetDistance. This matcher is used to match feature groups along the continuation. This is possible because a group of features is a vector of vectors, just like a StateSpaceSet is.
Return
agg_attractors_cont: likeattractors_cont, but mapping each group ID to the mergedStateSpaceSetof its members. (merged attractors are unions of the individual ones)centroids_cont: a vector of dictionaries mapping each group ID to the centroid (mean) of its members' feature vectors at that step.members_cont: a vector of dictionaries mapping each group ID to the vector of original attractor IDs (the keys ofattractors_cont[i]) merged into it at that step.
All three share consistent group IDs along the parameter axis.
Description
At each parameter step the attractors are turned into feature vectors with featurizer and partitioned into groups with group_config (a GroupingConfig); the members of a group are merged into a single StateSpaceSet. To keep group IDs consistent along the parameter curve, groups are matched between consecutive parameter steps in feature space: the set of member feature vectors of each group is collected into a StateSpaceSet and these are matched with MatchBySSSetDistance using the distance keyword. By default distance = Centroid(), i.e. groups are matched by the distance between their feature-vector centroids; any other set-distance accepted by setsofsets_distances (e.g. Hausdorff, StrictlyMinimumDistance, or a custom f(A, B)) instead compares the full feature-vector sets. Because matching is in feature space, featurizer must return numeric feature vectors (e.g. SVectors).
Aggregating stability quantifiers
To obtain stability quantifiers for the aggregated groups, pass agg_attractors_cont to stability_quantifiers_along_continuation. Each merged group is then treated as a single attractor, so every quantifier — including those that need the raw basin data, such as medians and critical shock magnitudes — is computed correctly for the group.
See the aggregation example for an illustration.
If you only care about aggregating the basin fractions, there is no reason to go through the route of stability_quantifiers_along_continuation. Simply give the returned members_cont, together with the fractions_cont from the continuation, to aggregate_fractions. The denser the sampling for the original continuation was, the more accurate the aggregated fractions will be, and there is no reason to re-run a continuation.
Attractors.aggregate_fractions — Function
aggregate_fractions(fractions_cont, members_cont)Simple convenience function that aggregates the basin fractions resulting from a global_continuation, given the members of each aggregated group. The latter is the output of aggregate_continuation.
Dict utils
Attractors.unique_keys — Function
unique_keys(v::Iterator{<:AbstractDict})Given a vector of dictionaries, return a sorted vector of the unique keys that are present across all dictionaries.
Attractors.swap_dict_keys! — Function
swap_dict_keys!(d::Dict, matching_map::Dict)Swap the keys of a dictionary d given a matching_map which maps old keys to new keys. Also ensure that a swap can happen at most once, e.g., if input d has a key 4, and rmap = Dict(4 => 3, 3 => 2), then the key 4 will be transformed to 3 and not further to 2.
Attractors.next_free_id — Function
next_free_id(new::Dict, old::Dict)Return the minimum key of the "new" dictionary that doesn't exist in the "old" dictionary. If one of the two dictionaries are empty, return its maximum key + 1. If both are empty, return 1.
The function assumes tha the dictionary keys are integers.
Matching attractors
Matching attractors follow an extendable interface based on IDMatcher. The available matchers are:
Attractors.MatchBySSSetDistance — Type
MatchBySSSetDistance(; distance = Centroid(), threshold = Inf, use_vanished = false)A matcher type that matches IDs by the distance of their corresponding state space sets.
Keyword arguments
distance = Centroid(): distance to match by, given tosetsofsets_distances.threshold = Inf: sets with distance larger than thethresholdare guaranteed to not be mapped to each other.use_vanished = !isinf(threshold): value of the keyworduse_vanishedwhen used inmatch_sequentially!.
Description
In this matcher the values compared are StateSpaceSets which in most cases represent attractors in the state space, but may also represent any other set such as a group of features.
Here is how this matcher works: (recall in this conversation that sets/attractors are stored in dictionaries, mapping keys/IDs to the sets, and we want to match keys in the "new" dictionary (a₊) to those in the "old" dictionary (a₋)).
The distance between all possible pairs of sets between the "old" sets and "new" sets is computed as a formal distance between sets. This is controlled by the distance option, itself given to the lower-level setsofsets_distances function, so distance can be whatever that function accepts. That is, one of Centroid, Hausdorff, StrictlyMinimumDistance, or any arbitrary user-provided function f that given two sets f(A, B) it returns a positive number (their distance).
Sets (in particular, their corresponding IDs) are then matched according to this distance. First, all possible ID pairs (old, new) are sorted according to the distance of their corresponding sets. The pair with smallest distance is matched. IDs in matched pairs are removed from the matching pool to ensure a unique mapping. Then, the next pair with least remaining distance is matched, and the process repeats until all pairs are exhausted.
Additionally, you can provide a threshold value. If the distance between two sets is larger than this threshold, then it is guaranteed that the two sets will get assigned different ID in the replacement map, and hence, the set in a₊ gets the next available integer as its ID.
Attractors.MatchByBasinEnclosure — Type
MatchByBasinEnclosure(; kw...) <: IDMatcherA matcher that matches attractors by whether they are enclosed in the basin of a new attractor or not.
Keyword arguments
ε = nothing: distance threshold given toBasinMapProximity. Ifnothing, it is estimated as a quarter of the minimum distance of centroids (in contrast to the default more accurate estimation inBasinMapProximity).Δt = 1, consecutive_lost_steps = 1000: also given toBasinMapProximity. We have not yet decided what should happen to attractors that did not converge to one of the current attractors within this number of steps. At the moment they get assigned the next available free ID but this may change in future releases.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 attraction 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 new attractor A₊ is "matched" (i.e., has its ID changed) to the old 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 BasinMapProximity bmap that maps initial conditions to the ₊ attractors when the trajectories from the initial conditions are ε-close to the ₊ attractors.
There can be the situation where multiple ₋ attractors converge 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 MatchBySSSetDistance accepts. The closest ₊ attractor gets the ID of the ₋ closest attractor that converge to it.
Basin enclosure is a concept similar to "basin (in)stability" in (Ritchie et al., 2023): attractors that quantify as "basin stable" are matched.
Attractors.MatchByBasinOverlap — Type
MatchByBasinOverlap(threshold = Inf)A matcher that matches IDs given full basins of attraction.
Description
This matcher cannot be used in with the generic global continuation method of AttractorSeedContinueMatch. This matcher matches IDs of attractors whose basins of attraction before and after b₋, b₊ have the most overlap (in pixels). This overlap is normalized in 0-1 (with 1 meaning 100% of a basin in b₋ is overlaping with some other basin in b₊). Therefore, the values this matcher compares are full basins of attraction, not attractors themselves (hence why it can't be given to AttractorSeedContinueMatch). Rather, you may use this matcher with matching_map.
The threshold can dissallow matching between basins that do not have enough overlap. Basins whose overlap is less than 1/threshold are guaranteed to get assined different IDs. For example: for threshold = 2 basins that have ≤ 50% overlap get different IDs guaranteed. By default, there is no threshold.
The information of the basins of attraction is typically an Array, or a ArrayBasinsOfAttraction type , i.e. the direct output of basins_of_attraction. For convenience, as well as backwards compatibility, when using matching_map with this bmap you may provide two Arrays b₊, b₋ representing basins of attraction after and before, and the conversion to dictionaries will happen internally as it is supposed to. Similarly two ArrayBasinsOfAttraction types for before and after can be used, BoA₊ and BoA₋. To replace the IDs in b₊ given the replacement map just call replace!(b₊, rmap...), or equivalently replace!(BoA₊.basins, rmap...), or use the in-place version matching_map! directly.
A lower-level input for this matcher in matching_map can be dictionaries mapping IDs to vectors of cartesian indices, where the indices mean which parts of the state space belong to which ID
Attractors.DontMatch — Type
DontMatch <: IDMatcher
DontMatch()Do no matching, i.e. return an empty dictionary when called in matching_map.
Matching interface
Attractors.IDMatcher — Type
IDMatcherSupertype of all "matchers" that match can IDs labelling attractors. Currently available matchers:
Matchers implement an extendable interface based on the function matching_map. This function is used by the higher level function match_sequentially!, which can be called after any call to a global continuation to match attractors differently, if the matching used originally during the continuation was not the best.
Attractors.matching_map — Function
matching_map(
a₊::Dict, a₋::Dict, matcher;
ds::DynamicalSystem, p, pprev, next_id
) → rmapGiven dictionaries a₊, a₋ mapping IDs to values, return a replacement map: a dictionary mapping the IDs (keys) in dictionary a₊ to IDs (keys) in dictionary a₋, so that so that values in a₊ that are the "closest" to values in a₋ get assigned the same key as in a₋. In this way keys of a₊ are "matched" to keys of a₋. Use swap_dict_keys! to apply rmap to a₊ or to other dictionaries with same keys as a₊.
How matching happens, i.e., how "closeness" is defined, depends on the algorithm matcher.
The values contained in a₊, a₋ can be anything supported by matcher. Within Attractors.jl they are typically StateSpaceSets representing attractors. Typically the +,- mean after and before some change of parameter of a dynamical system.
matching_map always returns an empty dictionary of either a₊, a₋ is empty.
Keyword arguments
ds: the dynamical system that generateda₊, a₋.p, pprev: the parameters corresponding toa₊, a₋. Both need to be iterables mapping parameter index to parameter value (such asDict, Vector{Pair}, etc., so whatever can be given as input toDynamicalSystems.set_parameters!).next_id = next_free_id(a₊, a₋): the ID to give to values ofa₊that cannot be matched toa₋and hence must obtain a new unique ID.
Some matchers like MatchBySSSetDistance do not utilize ds, p, pprev in any way while other matchers like MatchByBasinEnclosure do, and those require expliticly giving values to ds, p, pprev as their default values is just nothing.
matching_map(BoA₊::ArrayBasinsOfAttraction, BoA₋::ArrayBasinsOfAttraction, matcher::MatchByBasinOverlap)
matching_map(b₊::AbstractArray, b₋::AbstractArray, matcher::MatchByBasinOverlap)Special case of matching_map where instead of having as input dictionaries mapping IDs to values, we have Arrays which represent basins of attraction and whose elements are the IDs.
The second function signature exists for backwards compatibility.
See MatchByBasinOverlap for how matching works.
Attractors.matching_map! — Function
matching_map!(a₊, a₋, matcher; kw...) → rmapConvenience function that first calls matching_map and then replaces the IDs in a₊ with this rmap.
Attractors.match_sequentially! — Function
match_sequentially!(dicts::Vector{Dict{Int, Any}}, matcher::IDMatcher; kw...)Match the dicts, a vector of dictionaries mapping IDs (integers) to values, according to the given matcher by sequentially applying the matching_map function to all elements of dicts besides the first one.
In the context of Attractors.jl dicts are typically dictionaries mapping IDs to attractors (StateSpaceSets), however the function is generic and would work for any values that matcher works with.
Return rmaps, which is a vector of dictionaries. rmaps[i] contains the matching_map for dicts[i+1], i.e., the pairs of old => new IDs.
Keyword arguments
pcurve = nothing: the curve of parameters along which the continuation occured, from which to extract thep, pprevvalues given tomatching_map. Seeglobal_continuationif you are unsure what this means.ds = nothing: dynamical system propagated tomatching_map.retract_keys::Bool = true: Iftrueat the end the function will "retract" keys (i.e., make the integers smaller integers) so that all unique IDs are the 1-incremented positive integers. E.g., if the IDs where 1, 6, 8, they will become 1, 2, 3. The special ID -1 is unaffected by this.use_vanished = false: Ifuse_vanised = true, then IDs (and their corresponding sets) that existed before but have vanished are kept in "memory" when it comes to matching: the current dictionary values (the attractor sets) are compared to the latest instance of all values that have ever existed, each with a unique ID, and get matched to their closest ones. The value of this keyword is obtained from thematcher.
match_sequentially!(continuation_quantity::Vector{Dict}, rmaps::Vector{Dict})Do the same as in match_sequentially! above, now given the vector of matching maps, and for any arbitrary quantity that has been tracked in the global continuation. continuation_quantity can for example be fractions_cont from global_continuation.
Low-level distance functions
StateSpaceSets.Centroid — Type
Centroid(metric = Euclidean())A distance that can be used in set_distance. The Centroid method returns the distance (according to metric) between the centroids (a.k.a. centers of mass) of the sets.
metric can be any function that takes in two static vectors are returns a positive definite number to use as a distance (and typically is a Metric from Distances.jl).
StateSpaceSets.Hausdorff — Type
Hausdorff(metric = Euclidean())A distance that can be used in set_distance. The Hausdorff distance is the greatest of all the distances from a point in one set to the closest point in the other set. The distance is calculated with the metric given to Hausdorff which defaults to Euclidean.
Hausdorff is 2x slower than StrictlyMinimumDistance, however it is a proper metric in the space of sets of state space sets.
This metric only works for StateSpaceSets whose elements are SVectors.
For developers: set_distance can take keywords tree1, tree2 that are the KDTrees of the first and second sets respectively.
StateSpaceSets.StrictlyMinimumDistance — Type
StrictlyMinimumDistance([brute = false,] [metric = Euclidean(),])A distance that can be used in set_distance. The StrictlyMinimumDistance returns the minimum distance of all the distances from a point in one set to the closest point in the other set. The distance is calculated with the given metric.
The brute::Bool argument switches the computation between a KDTree-based version, or brute force (i.e., calculation of all distances and picking the smallest one). Brute force performs better for sets that are either large dimensional or have a small amount of points. Deciding a cutting point is not trivial, and is recommended to simply benchmark the set_distance function to make a decision.
If brute = false this metric only works for StateSpaceSets whose elements are SVectors.
For developers: set_distance can take a keyword tree2 that is the KDTree of the second set.
StateSpaceSets.set_distance — Function
set_distance(ssset1, ssset2 [, distance])Calculate a distance between two StateSpaceSets, i.e., a distance defined between sets of points, as dictated by distance.
Possible distance types are:
Centroid, which is the default, and 100s of times faster than the restHausdorffStrictlyMinimumDistance- Any function
f(A, B)that returns the distance between two state space setsA, B.
StateSpaceSets.setsofsets_distances — Function
setsofsets_distances(a₊, a₋ [, distance]) → distancesCalculate distances between sets of StateSpaceSets. Here a₊, a₋ are containers of StateSpaceSets, and the returned distances are dictionaries of distances. Specifically, distances[i][j] is the distance of the set in the i key of a₊ to the j key of a₋. Distances from a₋ to a₊ are not computed at all, assumming symmetry in the distance function.
The distance can be anything valid for set_distance.
Containers a₊, a₋ can be empty but they must be concretely typed.