How to construct structs with implicitly typed fields?

From the documentation it is not clear to me how to achieve something like the following:

mutable struct foo {T<:Int64, S<:Array{Float64}}

   function foo(size::T) where {T}
       matrix = zeros(size,size)

I tried a few things and I always run into “too few type parameters defined” or when I try new{T,S} new(size,matrix) “S not defined”.

What am I doing wrong?

There are actually several things that can be improved here in order to work.

  1. Int64 is a concrete type, so you shouldn’t try to create something that is its “child”. It is better to constrain it to Integer.

  2. What are you trying to accomplish exactly by subtyping the matrix as subtype from Array{Float64}? Do you mean that it should be able to accept only floating point numbers, or real numbers (including integers, rationals, etc)?

In any case, three things can be improved there:

2.A. Arrays are not covariant i.e.: Array{Int64,1} is not a subtype of Array{Integer,1}. The way to do that is matrix::Array{<:Integer,1}. The same is applied for Float.

2.B. It is better to subtype from AbstractArray, which also allows other things that act as Array although they are not exactly that.

2.C. The Array type needs two parameters: the element type (Float64, etc) and the dimensions (an integer). What you are looking for I think is AbstractMatrix{<:Float} or AbstractMatrix{<:Number}.

More things that yoyu may need to think about:

  1. Is it really necessary that the type is mutable? If you want to change the size, it may be better to create a new object altogether.

  2. Do you really need to create a type for this? Is not enough to have available Matrix{Float64} and its method size? If you want to ensure that it remains the same size, maybe you should take a look at StaticArrays.jl


Ok maybe I should clarify a little bit more what I want to achieve here. I have a very big and expensive ODE System (so I think StaticArrays won’t be beneficial for the performance.) I read the performance tips and as far as I understand I should localise as much variables as possible.

Therefore I want to pass some preallocated Arrays/ Vectors to the ODE-system where they will mutated in order to avoid heap allocations. I however want to be able to solve the system on different discretisation sizes, hence I need to be able to construct differently sized preallocation Arrays/Vectors when calling the ODE-system. So I use a closure like this:

ode_prob = ODEProblem(du, u,p,t) -> mysystem(du,u,p,t,AllocationStruct(size))

this works well with:

mutable struct AllocationStruct
   function AllocationStruct(size)
        matrix_foo = zeros(size,size)
        matrix_bar = similar(matrix_foo)
        vector_u = zeros(size)

So I try to construct some arrays/vectors that will contain reals from the size of the number discretisation points size which is obviously a integer.

In the performance tips I read that it would be better if I actually define the types of the fields of AllocationStruct and this is what I’m trying to achieve.

There is no need to use inner constructor here. Outer constructors are more safe, because they do not override default constructors.

1 Like

Are you looking for something like:

mutable struct AllocationStruct{R,S}

AllocationStruct(size) = AllocationStruct(zeros(size,size), zeros(size,size), zeros(size))

julia> AllocationStruct(4)
AllocationStruct{Matrix{Float64}, Vector{Float64}}([0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0], [0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0; 0.0 0.0 0.0 0.0], [0.0, 0.0, 0.0, 0.0])
1 Like

I think you can dismiss creating a whole type for this. Also, I don’t think you are using the closure right. If you want a function for preallocation, maybe something like this:

(n) -> (zeros(n,n),zeros(n,n),zeros(n))

Function new needs to know the concrete parameters of the struct that you are trying to create. When you try to use new{T,S}, the value of T is known from dispatch, but S is an undefined variable. The problem should be fixed if you use new{T, typeof(matrix)} instead.

Why do you need to store the size of the matrix as a separate field in the structure?
You can always access the size of the matrix using size(matrix). For an NxM matrix, it will return the tuple (N, M). If you want to acces the size along dimension dim, you can use size(matrix, dim). For example, size(matrix, 1) should return N.

1 Like