This Julia package provides an interface for defining a type's "dimensions". E.g., a vector with 3 elements (which might be a position in 3D Cartesian space) would have 3 dimensions, the "x", "y", and "z" parts. Essentially, every scalar making up a type could be a dimension of that type.
(This is notably different use of the word "dimension" from Julia's "array dimensions". A 3-element vector is made of up 3 scalars and hence has three independent dimensions in the nomenclature here, even though the Vector type is a "1D array".)
The key functionality comes from the following functions:
numdims(x)returns the number of dimensions ofxnumdims_for_type(T)returns the number of dimensions for values of typeT, when that is known from the type alonegetdim(x, k)returns dimensionkofxeachdim(x)returns an iterator over the dimensions ofxdimstyle(T)returns the dimension "style" of typeT
By default, this works with Real numbers (1D), Complex numbers (2D), enums (1D), and AbstractVectors of Real elements. It also works for SVectors if you have StaticArrays imported.
To add "dimensional" behavior to a custom type, just add methods for those functions for your type. And in fact, most types can be handled simply by adding a method to dimstyle. Here's an example:
struct Position
x::Float64
y::Float64
z::Float64
end
import Dimensions
Dimensions.dimstyle(::Type{Position}) = Dimensions.StructDimensionStyle()
The remaining functions are already implemented for that "style"; the dimensions will be taken as the combination of the dimensions of the fields. Here, numdims(x) and numdims_for_type(Position) will clearly be 3, getdim(x, 2) will return the y field, and eachdim(x) will return an iterator over x, then y, then z.
Further, structs of structs work just as well. Consider the following:
using StaticArrays: SVector
struct TranslationState
position::Position
velocity::SVector{3, Float64}
end
Dimensions.dimstyle(::Type{TranslationState}) = Dimensions.StructDimensionStyle()
That type has 6 dimensions, 3 from position and 3 from velocity.
This is useful for anything that needs to break a type all the way down to its fundamental scalars. For instance, when plotting a vector of Position over time, a plotting package could first make a line for each position's dimension 1, then a line for each position's dimension 2, and then for each position's dimension 3, giving three lines. More generally, it is a way of serializing the data to scalars.
numdims_for_type(T) is useful when the dimensionality is fixed by the type, such as Float64, ComplexF64, SVector{3, Float64}, or a StructDimensionStyle type whose fields all have type-known dimensionality. It is not available for value-dependent types like ordinary dynamic vectors; call numdims(x) on a value in those cases.
ScalarDimensionStyle: Always 1 dimensionalComplexDimensionStyle: Always 2 dimensionalRealVectorDimensionStyle: The dimensions are the elements of theAbstractVector(all real)StructDimensionStyle: The dimensions are the set containing the dimensions of the fields, recursively, all the way down. This works for most but not all types. An example where it doesn't work: the Rational type hasnumanddenfields, but it's really a single real dimension, not two separate dimensions.