# Mixing GPU/CPU in struct definition

Hello,
I use big structs in my code with lots of different vectors of integers and floats like so:

mutable struct Bodies
a:: Vector{StaticArrays.SVector{3,Float64}}
b:: Int
c:: Vector{StaticArrays.SVector{3,Int}}
d:: Int
e:: Vector{StaticArrays.SVector{4,Int}}
f:: Int
g:: Int
h:: Vector{Int}
i:: Vector{Union{ StaticArrays.SVector{3,Int},StaticArrays.SVector{4,Int} }}
j:: Vector{Vector{Int}}
...

Right now this code is running on the CPU. An older version without structs ran on the GPU and now I want to port this one to be CPU/GPU runnable.
How would you guys recommend I do that? Do I need to have 2 structs definitions, one using CuArrays and one with Vectors or is there a more elegant way to do this?
Thanks a lot!

You can use a single struct definition, with type parameters
https://docs.julialang.org/en/v1/manual/types/#Parametric-Types

1 Like

How does that work with Vectors and CuArrays?
I tried this:

mutable struct Bodies{V}
a :: V{StaticArrays.SVector{3,Float64}}
nPoints :: Int
c :: V{StaticArrays.SVector{3,Int}}
end

and it doesnâ€™t work.
Thanks a lot!

Yep, that doesnâ€™t work. The error such a definition throws (I use Dict instead of StaticArray to not need using a package):

julia> struct A{V}
a::V{Dict{Int,Int}}
end
ERROR: TypeError: in Type{...} expression, expected UnionAll, got a value of type TypeVar
Stacktrace:
[1] top-level scope
@ REPL[1]:1

is a bit cryptic. But it boils down to that types need to be fully defined within the type-def (i.e. a UnionAll and not a TypeVar). This means that you cannot do â€śtype calculationsâ€ť within the type-def. For instance this is not possible either:

julia> struct B{N}
a::Array{Int, N+1}
end
ERROR: MethodError: no method matching +(::TypeVar, ::Int64)
...

What you can do is:

julia> struct D{V<:U where U<:AbstractVector{D} where D<:Dict{Int,Int}}
a::V
b::V
end

julia> D([Dict(1=>1)], [Dict(2=>2)])
D{Vector{Dict{Int64, Int64}}}([Dict(1 => 1)], [Dict(2 => 2)])

or equivalently

julia> struct E{V<:AbstractVector{<:Dict{Int,Int}}}
a::V
b::V
end

julia> E([Dict(1=>1)], [Dict(2=>2)])
E{Vector{Dict{Int64, Int64}}}([Dict(1 => 1)], [Dict(2 => 2)])

However, for your case this approach may not be that useful as the types of your fields all seem a bit different. So, probably you just give each a type parameter (without any fancy & verbose constraints) and be done with it.

Ok, this is getting complicated. I think I might define two structs, as I donâ€™t want the definitions to be super messy.
Thanks a lot!

Yes, that is probably best, as too many type parameters are bad (e.g. really annoying in error messages). You can use something like this to avoid repetition:

julia> for (n,V) in zip([:CPUstuct, :GPUstruct], [:Vector, :CuVector])
eval(quote
struct \$n
a::\$V{StaticArrays.SVector{3,Float64}}
c::\$V{StaticArrays.SVector{20,Float64}}
end
end
)
end

(untested)

1 Like