Writing `broadcast_c` for struct with custom indexing

I have a custom type

struct State{T} <: AbstractArray{T,1}
    fields::Dict{Symbol, UnitRange{Int64}}
    arr::Array{T}
end

Base.size(S::State) = size(S.arr)
Base.IndexStyle(::Type{<:State}) = IndexLinear()
Base.getindex(S::State{T}, inds::Vararg{Int,1}) where {T} = S.arr[inds...]
Base.getindex(S::State{T}, ind::Symbol) where {T} = S.arr[S.fields[ind]]
Base.setindex!(S::State{T}, val, inds::Vararg{Int,1}) where {T} = (S.arr[inds...] = val)

that allows indexing with symbols, e.g.

julia> state0 = State(Dict([(:r, 1:3), (:v, 4:6)]), [10; 11; 12; 13; 14; 15])
6-element Constants.State{Int64}:
 10
 11
 12
 13
 14
 15

julia> state0[:r]
3-element Array{Int64,1}:
 10
 11
 12

I plan on using this with DifferentialEquations.jl, but I’m currently having trouble getting broadcasting to work. For example, I cannot do

julia> dstate = deepcopy(state0)
6-element Constants.State{Int64}:
 10
 11
 12
 13
 14
 15

julia> dstate[:r] .= 5.0
ERROR: ArgumentError: invalid index: r

I found Broadcast for custom type - #3 by fengyang.wang which suggested I do

Base.Broadcast._containertype(::Type{<:State}) = State
Base.Broadcast.promote_containertype(::Type{State}, _) = State
Base.Broadcast.promote_containertype(_, ::Type{State}) = State
Base.Broadcast.promote_containertype(::Type{State}, ::Type{State}) = State

but I’m having trouble implementing the last step, to write a Base.Broadcast.broadcast_c(f, ::Type{State}, _...) = ... that makes use of my symbol indexing. It looks like implementing custom broadcasting will be improved in 0.7, but I’m stuck with 0.6 for now.

I think the nicer way to handle this is to make broadcast just apply to the arr fields. I would make the fields be an OrderedDict so you have a canonical numbers for the array and then let broadcast run down the array directly, by passing the slow dictionary access.

BTW,

arr::Array{T}

that’s not type-stable since arrays need the dimension.

One thing to note too is that we have some proof-of-concept implementations of arrays with naming schemes here:

https://github.com/JuliaDiffEq/LabelledArrays.jl

On v0.7 we want to make that compile away the cost of named access via either a.b overloads or a[:b] literals, but for now it does have a small cost.

But even on v0.6 the StaticArray version should be fast.

1 Like

I’m looking at LabelledArrays.jl, except I’m getting an error running the example code for constructing LMArrays and LArrays (also, I think the single line constructor macro names are wrong in the README).

Copy pasting

names = @SArray [:a,:b,:c]
values = @MArray [1,2,3]

gives

julia> A = LMArray(names,values)
3-element LArray{Tuple{3},Int64,1,3}:
Error showing value of type LArray{Tuple{3},Int64,1,3}:
ERROR: ArgumentError: invalid index: (1, 1)

Additionally, would this allow indexing by multiple names at once? e.g. A[:a,:b]? I often need access to several elements at once for easy vector math (:r -> :rx, :ry, :rz).

That error is just in the display. It’s being made correctly but it’s not printing correctly. That’s fine though: the library is not released and still being worked on so think of it as a preview.

It doesn’t handle this. That’s a good idea though.

Do I have to do anything special to use LMArray in an in-place function to integrate? Trying to solve an ODEProblem this gives a ‘no method matching’ error that looks related to uType.

Looks like this library still needs work. Now that I know that someone is interested I’ll move it up the priority list. Ping me every once in awhile here or in the chat to force me to do it :stuck_out_tongue:

The friendliest of pings: :ping_pong:

LabelledArrays works and is registered now.

Julia changed its broadcasting implementation. You can now read about it here. Interfaces · The Julia Language

2 Likes