How do I define a struct type such that it is limited to one of a few select options?
How do I define a struct type such that it can only take integer values?
How do I programmatically define a static rank-N tensor of specified proportions?
The longer version:
I am struggling in defining a struct which contains a member variable, m, which is a rank-N tensor (typeof(N) <: Integer) of a fixed size, L where typeof(L) <: Integer, for all dimensions which contains data of type T (limited to Float32/Float64/ComplexF32/ComplexF64), i.e. for a placeholding value X::T
N = 0 results in m = X (scalar)
N = 1, L = 3 results in m = [X, X, X] (vector)
N = 2, L = 2 results in m = [[X, X], [X, X]] (matrix)
etc.
In order to achieve this, I must:
Limit the data type T to one of a select few
Define N and L within the struct types such that they can only take integer values (if this is even possible)
Programmatically define a rank-N tensor
My thought processes so far:
I initially attempted to harness the functionality and efficiency provided by StaticArrays.jl through e.g. SVector{L, T}(fill(X, L)) SMatrix{L, L, T}(fill(X, (L, L))),
however, these are respectively specific to N = 1 and N = 2, so this is clearly not generalised to N dimensions.
I found that generic SArrays can be generalised to N dimensions, e.g. SArray{Tuple{L}, T}(fill(X, L)) SArray{Tuple{L, L}, T}(fill(X, (L, L))),
However, this still requires specification with the correct number of arguments.
My guess in resolving this shortcoming would be to utilise the repeat() function somehow, however I am not seeing how to do so.
My usage of structs in julia is pretty rudimentary, but ultimately I assume the struct would look akin to:
struct My_Struct{T, N, L} where T \in [Float32, Float64, ComplexF32, ComplexF64], typeof(N) <: Integer, typeof(L) <: Integer
m::SArray{Tuple{repeat([X], L)}, T}
end
The idiom for that is a guarded inner constructor:
struct MyStruct{T, N, L}
m::Array{T}
function MyStruct{T, N, L}(m) where {T,N,L}
@assert N isa Integer
@assert L isa Integer
new{T, N, L}(m)
end
end
julia> MyStruct{Float64, 2, 3}(rand(3,3))
...
julia> MyStruct{Float64, 2.0, 3}(rand(3,3))
ERROR: AssertionError: N isa Integer
@sijo Many thanks, that appears to be what I am looking for, and at least I now better understand how Union can be used! Regarding flexibility, I don’t envisage requiring further options than those specified in my OP, but maybe those are famous last words
@thisrod Is there a way to non-explicitly implement those assertions into every constructor (I will be creating various constructors as I suspect users will desire the ability to fall back on default values), or is it a case of concision vs. rigour?
You would normally define the convenience methods as outer constructors, so this happens automatically. The outer constructors are ordinary Julia methods, which call a privileged inner constructor method, which does the checks and creates the MyStruct.
Every value of type MyStruct was created when an inner constructor returned it. If you explicitly define an inner constructor in the type definition, Julia doesn’t define any others, and there are no MyStructs except for the ones returned by your constructor. If you don’t, Julia defines the obvious inner constructors by default.