I am trying to implement custom broadcasting for a type, and after spending a few hours with the doc I am well and truly lost.
My type looks like this:
struct XYZ{T}
data::Matrix{Union{T,Nothing}}
x::Number
y::Number
end
I am mostly accessing elements in data
using Cartesian indexing, but the very important point is that I would like the values with nothing
to be omitted (it’s not a sparse array because 99% of the time the degree of sparsity makes them less efficient, and because T
can be an arbitrary type).
I have tried to define
Base.size(R::XYZ) = size(R.data)
Base.length(R::XYZ) = length(R.data)
Base.getindex(R::XYZ{T}, inds::Vararg{Int,2}) where {T} = R.data[inds...]
Base.setindex!(R::XYZ{T}, val, inds::Vararg{Int,2}) where {T} = R.data[inds...] = val
Base.broadcastable(R::XYZ) = R
Base.BroadcastStyle(::Type{XYZ}) = Broadcast.Style{XYZ}()
Base.axes(R::XYZ) = tuple(findall(!isnothing, R.data))
and then
function Base.similar(bc::Broadcast.Broadcasted{Broadcast.Style{XYZ}}, ::Type{ElType}) where ElType
A = find_entry(bc)
XYZ(similar(Array{Union{Nothing,ElType}}, size(bc)), A.x, A.y)
end
where find_entry
is inspired by the docs:
find_entry(bc::Base.Broadcast.Broadcasted) = find_entry(bc.args)
find_entry(args::Tuple) = find_entry(find_entry(args[1]), Base.tail(args))
find_entry(x) = x
find_entry(::Tuple{}) = nothing
find_entry(r::XYZ, rest) = r
find_entry(::Any, rest) = find_entry(rest)
But then I get the following error message:
julia> x .+ 1
ERROR: ArgumentError: broadcasting requires an assigned BroadcastStyle
Stacktrace:
[1] copy(bc::Base.Broadcast.Broadcasted{Base.Broadcast.Unknown, Tuple{Vector{CartesianIndex{2}}}, typeof(+), Tuple{XYZ{Int64}, Int64}})
@ Base.Broadcast ./broadcast.jl:899
[2] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.Unknown, Nothing, typeof(+), Tuple{XYZ{Int64}, Int64}})
@ Base.Broadcast ./broadcast.jl:883
[3] top-level scope
@ REPL[146]:1
There’s probably something obvious I am missing, but I’m 100% lost on this one…