After spending quite a bit of time in the Customizing broadcasting section of the doc, reading a few questions here and trawling through the source code of StaticArrays.jl I’m throwing the sponge. (Once I’ve figured this out, I’ll try to help make this broadcasting section of the docs more accessible for the lay person like me…).
My specs are pretty simple:
struct MyType{R} <: AbstractVector{R}
data::Matrix{R}
metadata
end
I’d like to be able to do what’s required so that a user can do f.(x)
where x
is an instance of MyType
. This is pretty much identical to the ArrayAndChar
example from the docs with one difference: data
is a Matrix, elements of MyType are the rows. Something like
Base.length(m::MyType) = size(m.data, 1)
Base.getindex(m::MyType, i::Integer) = m.data[i,:]
Base.getindex(m::MyType, I::AbstractVector{<:Integer}) = MyType(m.data[I, :], m.metadata)
What I’d like to do is that f.(x)
applies f
to rows of x.data
. I don’t need operations etc, just this broadcasting rule. As a toy example, I’d like to be able to do maximum.(x)
and get a vector of maxes i.e. similar as the [maximum(x[i]) for i in 1:length(x)]
. (In practice I’d like f
to make use of the metadata but that doesn’t seem to be the hard bit)
After thoroughly confusing myself between the custom similar
and copyto!
, I admit I don’t understand what’s going on, help would be very welcome, thanks!
4 Likes
Your type definition does not agree with your specification. Either you want
struct MyType{RowType} <: AbstractVector{RowType}
data::Vector{RowType}
metadata
end
or you want
struct MyType{ElType} <: AbstractVector{Vector{ElType}}
data::Matrix{ElType}
metadata
end
In the second case, you might further want to replace Vector{ElType}
with a view for efficiency, but that is another layer of complexity which we can get into later.
4 Likes
With this definition:
and a method for size
, simple broadcasting should work without any customising:
Base.size(x::MyType) = (size(x.data, 1),)
Base.getindex(x::MyType, i::Integer) = x.data[i,:]
m42 = MyType(rand(Int8,4,2), nothing)
maximum.(m42) # works
How is metadata
going to be used?
1 Like
Thanks a lot for your answers, I understand the original mistake. There may be a further spec I should have mentioned.
What I’d like is that the data
is either a Vector{R}
or a Matrix{R}
. In the first case the type should be subtype of AbstractVector{R}
, in the second AbstractVector{Vector{R}}
as per @ettersi’s comment. I understand from both your comments that if this subtyping is done properly I wouldn’t have to bother with similar
and copyto!
.
In non-valid code I guess I’d like to have something like
struct MyType{R,E<:Union{R,Vector{R}}} <: AbstractVector{E}
data::Array{R, E == R ? 1 : 2}
metadata
end
Edit: the following seems to do what I want
struct MyType3{R,D,E<:Union{R,Vector{R}}} <: AbstractVector{E}
data::Array{R,D}
MyType3(m::Matrix{R}) where R = new{R,2,Vector{R}}(m)
MyType3(v::Vector{R}) where R = new{R,1,R}(v)
end
mt2 = MyType3(rand(5, 2));
mt1 = MyType3(rand(5));
Base.size(m::MyType3) = (size(m.data,1),)
Base.length(m::MyType3) = size(m.data,1)
Base.getindex(m::MyType3{R,1}, i::Int) where R = m.data[i]
Base.getindex(m::MyType3{R,2}, i::Int) where R = m.data[i,:]
mt2[1]
mt1[1]
foo(x) = sum(x.^2)
foo.(mt1)
foo.(mt2)
I am completely new to this topic and happened to find the discussions here.
I have a question regarding subtypes here: with the <:
syntax, are we saying that our custom type is a subtype of AbstractVector{R}
or AbstractVector{Vector{ElType}}
? And why do we need to explicitly write this, otherwise the custom broadcasting won’t work?
In a more general context, do we need to specify the abstract supertypes for our custom types?
No, you don’t need to subtype anything in order to participate in or customize broadcasting. But if you do, you must behave as we’d expect that abstract type to behave.
Subtyping <: AbstractArray
gives you lots of behaviors “for free,” but you must obey the rules for an AbstractArray in order for those behaviors to work. Whether you use <: AbstractVector{R}
or AbstractVector{Vector{ElType}}
or something different entirely all comes down to how you want your type to behave.
3 Likes