# Structure type + in place modification of columns of arrays

In this post, I have two questions I am not sure are completely separate or not, so I ask them both here.

I define a structure and a substructure that has a field `B` which is an array of `Distributions`. It can be univariate, product, multivariate etc.
I define the structure like that

``````using Distributions
abstract type mainT{F<:VariateForm} end

struct SubT{F} <: mainT{F}
B::AbstractArray{Distribution{F}}
end
``````

However, when I try to define a `SubT` from an array of `Bernoulli` it does not work

``````K = 5
T = 2
B = Bernoulli.(rand(K,T)) # Array of Bernoulli distributions
h = SubT(B) # Fail
ERROR: MethodError: no method matching SubT(::Matrix{Bernoulli{Float64}})
Closest candidates are:
SubT(::AbstractArray{Distribution{F, S} where S<:ValueSupport, N} where N) where F at REPL:2

``````

So I can do the following to make it work

``````B = convert(Matrix{UnivariateDistribution{S} where S<:ValueSupport}, B)
h = SubT2(B) # Works
``````

This type re-definition is problematic for many reason so I would like to avoid and be able to define my `h` the first way. Ideally, the type of `h` would not be `UnivariateDistribution{S} where S<:ValueSupport`but specific to each choice of distribution, e.g. here `Matrix{Bernoulli{Float64}}`.

The second problem is, when I want to sort columns of my array in some specific way with in place modification. I have the function

``````function sort_wrt_ref!(B::AbstractVector)
K = size(B, 1)
sorting = [succprob(h.B[k]) for k in 1:K]
B[:] = B[sortperm(sorting)]
end
``````

However, it does not seem to modify in place the columns of my array.

``````julia> h.B
5×2 Matrix{UnivariateDistribution{S} where S<:ValueSupport}:
Bernoulli{Float64}(p=0.958907)   Bernoulli{Float64}(p=0.204364)
Bernoulli{Float64}(p=0.0382291)  Bernoulli{Float64}(p=0.898818)
Bernoulli{Float64}(p=0.953247)   Bernoulli{Float64}(p=0.367522)
Bernoulli{Float64}(p=0.196388)   Bernoulli{Float64}(p=0.0861771)
Bernoulli{Float64}(p=0.333265)   Bernoulli{Float64}(p=0.275637)

julia> sort_wrt_ref!(h.B[:,1]) # it is sorting as the output shows but not changing the column h.B[:,1]
5-element Vector{UnivariateDistribution{S} where S<:ValueSupport}:
Bernoulli{Float64}(p=0.038229098624871005)
Bernoulli{Float64}(p=0.1963878250239648)
Bernoulli{Float64}(p=0.3332646196249027)
Bernoulli{Float64}(p=0.9532465128727561)
Bernoulli{Float64}(p=0.958907107799279)

julia> h.B
5×2 Matrix{UnivariateDistribution{S} where S<:ValueSupport}:
Bernoulli{Float64}(p=0.958907)   Bernoulli{Float64}(p=0.204364)
Bernoulli{Float64}(p=0.0382291)  Bernoulli{Float64}(p=0.898818)
Bernoulli{Float64}(p=0.953247)   Bernoulli{Float64}(p=0.367522)
Bernoulli{Float64}(p=0.196388)   Bernoulli{Float64}(p=0.0861771)
Bernoulli{Float64}(p=0.333265)   Bernoulli{Float64}(p=0.275637)
``````

I have the same issue with other in place function. They work as expected on `vector` but not on slice of array.

For your first problem, you are looking for:

``````struct SubT1{F, N}
# Not AbstractArray - use a concrete type
# The eltype of B is a subtype of Distribution
B :: Array{<: Distribution{F}, N}
end

K = 5
T = 2
B = Bernoulli.(rand(K,T))

h = SubT1(B)
``````
2 Likes

For your second problem, the issue is that `B[:, 1]` creates a copy of that column. One solution is to sort a `@view` instead. MWE:

``````julia> B0 = [4 3; 2 1];

julia> function sort_wrt_ref!(B::AbstractVector{F}) where F
K = size(B, 1)
B[:] = B[sortperm(B)]
end
sort_wrt_ref! (generic function with 1 method)

julia> B = copy(B0);

julia> sort_wrt_ref!(B[:, 1])
2-element Vector{Int64}:
2
4

julia> @show B
B = [4 3; 2 1]
2×2 Matrix{Int64}:
4  3
2  1

julia> B = copy(B0);

julia> sort_wrt_ref!(@view B[:, 1])
2-element Vector{Int64}:
2
4

julia> @show B
B = [2 3; 4 1]
2×2 Matrix{Int64}:
2  3
4  1
``````
2 Likes

Thanks that works!

1. Why should one use a concrete type instead of Abstract type?
2. Ok.

Your first problem occurs because `Matrix{Bernoulli{Float64}}` is not a subtype of `AbstractArray{Distribution}` Just like `Array{Float64}` is not a subtype of `AbstractArray{Real}`. The key change is the use of `{<:Distribution{F}}` as opposed to your original definition.

I don’t think the change from `AbstractArray` to `Array` is strictly necessary for the code to run, but generally, you want to avoid putting abstract types in structs for performance reasons since it adds an extra layer of indirection every time you access a structure member.