Hello all, I’m very new to julia and trying to essentially make a wrapper for a sparse-type array.
When I set up the type to inherit from AbstractSparseArrays, operations with non-sparse arrays output a full array.
using SparseArrays
struct SparseStruct{T,N} <: AbstractSparseArray{T,Int,N}
data::AbstractSparseArray{T,Int,N}
dims::NTuple{N,Int}
end
# forwarding some base methods
Base.size(A::SparseStruct) = A.dims
Base.getindex(A::SparseStruct, I::Any) = getindex(A.data, I)
Base.display(A::SparseStruct) = display(A.data)
a = SparseStruct(sprand(20,20,0.1), (20,20))
println(typeof(a * 2)) # produces full array
println(typeof(a .* sparse([2]))) # produces sparse array
This is different to how a normal sparse array acts:
println(typeof(a.data * 2)) # produces a sparse array.
My assumption was that by inheriting the AbstractSparseArray would lead to SparseArray-like functionality.
Thanks in advance
I believe you would need to implement multiplication for your struct something like:
Base.:*(a::SparseStruct, b::SparseStruct) = SparseStruct(a.data * b.data, a.dims)
You might want to do some error checking that the dims are equal and possibly add a where clause to ensure that T and N are the same for both inputs(if you want that).
1 Like
Thank you for the quick reply.
Your suggestion solved part of the problem, however i ran into a similar problem trying to define broadcasted functions.
Do you have a suggestion for the best way to bootsrap on top of SparseArrays inbuilt broadcasting behaviour?
From Interfaces in the manual it says you’d have to redefine Base.BroadcastingStyle and Base.Similar.
I thought I could try to get piggyback on SparseArrays with
Base.BroadcastStyle(::Type{SparseStruct{T,N}}) where {T,N} = SparseArrays.HigherOrderFns.SparseMatStyle()
But that gave me the following error which I can’t get my head around:
ERROR: MethodError: no method matching _copy(::SparseArrays.HigherOrderFns.var"#3#4"{typeof(*),SparseArrays.HigherOrderFns.var"#17#20"{SparseConnection{Float64,2},SparseArrays.HigherOrderFns.var"#23#26"{Int64}}})
And i’m similarly stuck with trying to redefine Base.similar
Uh, we’re out of my comfort zone, but based on my read of the “Customizing broadcasting” section of:
https://docs.julialang.org/en/v1/manual/interfaces/
What I would do is:
# Your explicit call of a function might be better, I don't know.
Base.BroadcastStyle(::Type{SparseStruct{T, N}) where {T, N} =
BroadcastStyle(AbstractSparseArray{T, Int, N})
Base.axes(x::SparseStruct) = axes(x.data)
Base.broadcastable(x::SparseStruct) = broadcastable(x.data)
i’m really unsure about the similar
function. My best guess would be:
Base.similar(bc::SparseStruct{T, N}, ::Type{SparseStruct{T, N}) where {T, N} =
SparseStruct(similar(bc.data, typeof(bc.data)), bc.dims)
But that is really a guess.
1 Like
Welcome to Julia! You’re diving straight into the deep end here… and there are some sharks in the pool.
In short: subtyping of AbstractSparseArray
isn’t magic and isn’t a fully formed and public API. You’ll probably have the most success in trying to duplicate how SparseMatrixCSC
works, but that relies on lots of internally goodness.
4 Likes
Okay, thanks for your advice,
In retrospect this was actually a pretty contrived way to go about what I wanted to do, subtyping a base abstract type initially seemed like a simple option, but you’re right it quickly got much more messy than I’d anticipated.