|
| 1 | +using Neighborhood |
| 2 | +export BasinsOfAttraction, |
| 3 | + ArrayBasinsOfAttraction, |
| 4 | + SampledBasinsOfAttraction, |
| 5 | + extract_basins, |
| 6 | + extract_basins, |
| 7 | + extract_domain |
| 8 | + |
| 9 | +######################################################################################### |
| 10 | +# Basins of Attraction structure definition |
| 11 | +######################################################################################### |
| 12 | +""" |
| 13 | + BasinsOfAttraction |
| 14 | +
|
| 15 | +A subtype of `BasinsOfAttraction` is a convenient structure that stores a representation |
| 16 | +of the `basins` of attraction, their associated `attractors`, and a representation of the domain |
| 17 | +over which the basin is defined. For example, this domain could be a `Grid` subtype matching |
| 18 | +the size of the basins as an array or a set of points sampled from the state space. These fields |
| 19 | +can be accessed using the [`extract_basins`](@ref), [`extract_attractors`](@ref), |
| 20 | +and [`extract_domain`](@ref) functions respectively. |
| 21 | +
|
| 22 | +Currently available subtypes: |
| 23 | +
|
| 24 | +* [`ArrayBasinsOfAttraction`](@ref) |
| 25 | +* [`SampledBasinsOfAttraction`](@ref) |
| 26 | +
|
| 27 | +All `BasinsOfAttraction` subtypes can be used with [`basins_fractions`](@ref) provided |
| 28 | +that the basins are represented as subtypes of `AbstractArray`. Additionally, all |
| 29 | +`BasinsOfAttraction` subtypes are iterable in the sense: `basins, attractors = BoA`, this |
| 30 | +was done to ensure backwards compatibility for functions whose original return format was |
| 31 | +`basins, attractors` but has since been replaced with a `BasinsOfAttraction` type. |
| 32 | +
|
| 33 | +The [`map_to_basin`](@ref) function provides simple interpolation of a point in state space |
| 34 | +to determine which basin of attraction it is likely to belong to. |
| 35 | +""" |
| 36 | +abstract type BasinsOfAttraction{ID} end |
| 37 | + |
| 38 | + |
| 39 | +""" |
| 40 | + ArrayBasinsOfAttraction(basins, attractors, grid) |
| 41 | +
|
| 42 | +A subtype of [`BasinsOfAttraction`](@ref) whose `basins` of attraction are represented by an |
| 43 | +`array::AbstractArray`, that has `D` number of dimensions. The `attractors` take the form of a |
| 44 | +dictionary mapping attractor labels to `StateSpaceSet`'s with the points of each set being of |
| 45 | +length `D`. The `grid` represents the spatial domain, and can be anything given to [`AttractorsViaRecurrences`](@ref) |
| 46 | +as a grid, i.e., a tuple of ranges or a `Grid` type. |
| 47 | +
|
| 48 | +""" |
| 49 | +struct ArrayBasinsOfAttraction{ID, D, B <: AbstractArray{ID,D}, T, V, G <: Grid, AK, S <: StateSpaceSet{D, T, V}} <: BasinsOfAttraction{ID} |
| 50 | + basins::B |
| 51 | + attractors::Dict{AK, S} |
| 52 | + grid::G |
| 53 | + |
| 54 | + function ArrayBasinsOfAttraction(basins::B, attractors::Dict{AK, S}, grid::G) where {ID, D, B <: AbstractArray{ID,D}, T, V, G <: Grid, AK, S <: StateSpaceSet{D, T, V}} |
| 55 | + # Dimensionality checks |
| 56 | + length(grid.grid) != ndims(basins) && error("The basins and the grid must have the same number of dimensions") |
| 57 | + # Attractor state space sets have the same type so same dimensions, can compare grid with any of them |
| 58 | + if !isempty(attractors) # |
| 59 | + length(grid.grid) != length(valtype(collect(values(attractors))[1])) && error("The attractor points and the grid must have the same number of dimensions") |
| 60 | + end |
| 61 | + new{ID,D,B,T,V,G,AK,S}(basins, attractors, grid) |
| 62 | + end |
| 63 | +end |
| 64 | +# The definition of other constructors can be found in `basins/basins_utilities.jl`. |
| 65 | + |
| 66 | +""" |
| 67 | + SampledBasinsOfAttraction(basins, attractors, sampled_points) |
| 68 | +
|
| 69 | +A subtype of [`BasinsOfAttraction`](@ref) whose `basins` of attraction are represented by a `vector::AbstractVector`. |
| 70 | +The `attractors` take the form of a dictionary mapping attractor labels to `StateSpaceSet`'s with |
| 71 | +the points of each set being of equal length. The spatial domain of this basin type is `sampled_points` which |
| 72 | +can be a `StateSpaceSet` with the same dimensionality, element type, and vector type as those |
| 73 | +used to represent the attractors or alternatively a vector of points with the aforementioned requirements. |
| 74 | +
|
| 75 | +Additional keyword arguments may be specified for use in the construction of a search structure which [`map_to_basin`](@ref) |
| 76 | +uses to interpolate state space points to their nearest basin. These arguments are: |
| 77 | +
|
| 78 | +* `tree`: search tree constructor (e.g. `KDTree`, `BallTree`) |
| 79 | +* `metric`: distance metric (e.g. `Euclidean()`, `Chebyshev()`) |
| 80 | +* `searchstructure_kwargs...`: additional keyword arguments passed to `searchstructure` |
| 81 | +""" |
| 82 | +struct SampledBasinsOfAttraction{ID, D, T, V <: AbstractVector, AK, S <: StateSpaceSet{D, T, V}, ss <: Neighborhood.SearchType} <: BasinsOfAttraction{ID} |
| 83 | + points_ids::Vector{ID} |
| 84 | + attractors::Dict{AK, S} |
| 85 | + sampled_points::S |
| 86 | + search_struct::ss |
| 87 | + |
| 88 | + function SampledBasinsOfAttraction(basins::Vector{ID}, attractors::Dict{AK, S}, sampled_points::S; tree = KDTree, metric = Euclidean(), ss_kwargs...) where |
| 89 | + {ID, D, T, V <: AbstractVector, AK, S <: StateSpaceSet{D, T, V}} |
| 90 | + # Dimensionality checks |
| 91 | + length(basins) != sampled_points && error("The basins and the sampled points must have equal length") |
| 92 | + search_struct = searchstructure(tree, BoA.sampled_points, metric, ss_kwargs...) |
| 93 | + new{ID,D,T,V,AK,S,typeof(search_struct)}(basins, attractors, sampled_points, search_struct) |
| 94 | + end |
| 95 | +end |
| 96 | +# The definition of other constructors can be found in `basins/basins_utilities.jl`. |
| 97 | + |
| 98 | +######################################################################################### |
| 99 | +# Basins of Attraction Convenience functions |
| 100 | +######################################################################################### |
| 101 | +Base.iterate(BoA::BasinsOfAttraction, state=1) = state == 1 ? (extract_basins(BoA), 2) : state == 2 ? (extract_attractors(BoA), 3) : nothing |
| 102 | + |
| 103 | +""" |
| 104 | + extract_basins(BoA::BasinsOfAttraction) → basins |
| 105 | +
|
| 106 | +Returns the basins component of a `BasinsOfAttraction` object. |
| 107 | +""" |
| 108 | +extract_basins(BoA::ArrayBasinsOfAttraction) = BoA.basins |
| 109 | +extract_basins(BoA::SampledBasinsOfAttraction) = BoA.points_ids |
| 110 | + |
| 111 | +""" |
| 112 | + extract_attractors(BoA::BasinsOfAttraction) → attractors |
| 113 | +
|
| 114 | +Returns the attractors component of a `BasinsOfAttraction` object. Which is a dictionary mapping |
| 115 | +attractor labels to attractors represented as `StateSpaceSet`'s. |
| 116 | +""" |
| 117 | +extract_attractors(BoA::ArrayBasinsOfAttraction) = BoA.attractors |
| 118 | +extract_attractors(BoA::SampledBasinsOfAttraction) = BoA.attractors |
| 119 | + |
| 120 | +""" |
| 121 | + extract_domain(BoA::BasinsOfAttraction) → domain |
| 122 | +
|
| 123 | +Returns the domain component of a `BasinsOfAttraction` object. |
| 124 | +""" |
| 125 | +extract_domain(BoA::SampledBasinsOfAttraction) = BoA.sampled_points |
| 126 | +extract_domain(BoA::ArrayBasinsOfAttraction) = BoA.grid |
| 127 | + |
| 128 | +##################################################################################### |
| 129 | +# Pretty printing |
| 130 | +##################################################################################### |
| 131 | +function Base.show(io::IO, BoA::ArrayBasinsOfAttraction) |
| 132 | + ps = 14 |
| 133 | + println(io, "$(nameof(typeof(BoA)))") |
| 134 | + println(io, rpad(" ID type: ", ps), typeof(BoA).parameters[1]) |
| 135 | + println(io, rpad(" basin size: ", ps), size(BoA.basins)) |
| 136 | + println(io, rpad(" grid: ", ps), extract_domain(BoA)) |
| 137 | + attstrings = split(sprint(show, MIME"text/plain"(), extract_attractors(BoA)), '\n') |
| 138 | + println(io, rpad(" attractors: ", ps), attstrings[1]) |
| 139 | + for j in 2:size(attstrings,1) |
| 140 | + println(io, rpad(" ", ps), attstrings[j]) |
| 141 | + end |
| 142 | + return |
| 143 | +end |
| 144 | + |
| 145 | +# Add first few vectors to printing followed by ... |
| 146 | +function Base.show(io::IO, BoA::SampledBasinsOfAttraction) |
| 147 | + ps = 14 |
| 148 | + println(io, "$(nameof(typeof(BoA)))") |
| 149 | + println(io, rpad(" ID type: ", ps), typeof(BoA).parameters[1]) |
| 150 | + println(io, rpad(" basin length: ", ps), length(BoA)) |
| 151 | + attstrings = split(sprint(show, MIME"text/plain"(), extract_attractors(BoA)), '\n') |
| 152 | + println(io, rpad(" attractors: ", ps), attstrings[1]) |
| 153 | + for j in 2:size(attstrings,1) |
| 154 | + println(io, rpad(" ", ps), attstrings[j]) |
| 155 | + end |
| 156 | + return |
| 157 | +end |
0 commit comments