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.