Struct field as view of another field

Hello,

Is it is possible to have a struct field that is a view of another field? A minimum working example is:

Base.@kwdef struct TestStruct
    a::Vector{Float64}
    b::Vector{Float64} = @view a[1:2]
end

t = TestStruct(a=[1., 2, 3])
t.b[1] = 10
t.a[1] == 10

I would expect/want (from my limited Julia experience and understanding of views) the last line to return true, i.e. that any modifications to b are reflected in a and vice versa.

Why do I want this?

I am carrying out a problem that involves optimising a Cholesky decomposition of a matrix to fit a multivariate normal distribution. In order to train the model, it is useful to be able to loop over the upper/lower elements of the triangular matrix, so I created a vector which is a view of the non-zero elements of the triangular matrix. I’d like to be able to e.g. use @functor on my struct, e.g. Flux.@functor Parameters (μ, triangular_flattened_view), to be able to conveniently modify the model parameters, with it automatically updating the triangular matrix so it can be used in calculations.

Hopefully, this makes sense and let me know if there is a more sensible way to go about it!

You define the type of b to be Vector so the view will be converted to a normal array.

julia> a = [1., 2., 3.];

julia> b = convert(Vector{Float64}, @view a[1:2]);

julia> b[1] = 10
10

julia> a[1]
1.0
2 Likes

Great! Thanks a lot, that makes perfect sense.

Just a note for others if they come across this, changing the type of b to AbstractVector{Float64} is sufficient to make the example work as I intended.

1 Like

Yes, but then you have an abstract field so performance will suffer. You could either explicitly make it a subarray field type, or you could parameterize the struct on that type.

2 Likes

Note that in that the conversion copies the array (so it will not be anymore a view of the other). My suggestion would be, to avoid abstract types and still accept the view:

julia> Base.@kwdef struct TestStruct{T}
           a::Vector{Float64}
           b::T = @view a[1:2]
       end

julia> t = TestStruct(a=[1., 2, 3])
TestStruct{SubArray{Float64, 1, Vector{Float64}, Tuple{UnitRange{Int64}}, true}}([1.0, 2.0, 3.0], [1.0, 2.0])

julia> t.b[1] = 10
10

julia> t.a[1] == 10
true

(edit: this is the “parameterize the struct on that type” mentioned by @kristoffer.carlsson)

2 Likes