Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CUSPARSE does not support integer matrices, breaks printing #1402

Closed
CarloLucibello opened this issue Feb 24, 2022 · 9 comments · Fixed by #1410
Closed

CUSPARSE does not support integer matrices, breaks printing #1402

CarloLucibello opened this issue Feb 24, 2022 · 9 comments · Fixed by #1410
Labels
bug Something isn't working

Comments

@CarloLucibello
Copy link
Contributor

On cpu you can contruct a sparse matrix using either integer or float value types

julia> sparse([1, 2], [2, 1], [5.0, 5.0])
2×2 SparseMatrixCSC{Float64, Int64} with 2 stored entries:
     5.0
 5.0    

julia> sparse([1, 2], [2, 1], [5, 5])
2×2 SparseMatrixCSC{Int64, Int64} with 2 stored entries:
   5
 5  

On gpu, integer value types fail

julia> sparse([1, 2] |> cu, [2, 1] |> cu, [5.0, 5.0] |> cu)
2×2 CUDA.CUSPARSE.CuSparseMatrixCSC{Float32, Int32} with 2 stored entries:
     5.0
 5.0    

julia> sparse([1, 2] |> cu, [2, 1] |> cu, [5, 5] |> cu)
ERROR: MethodError: no method matching (CUDA.CUSPARSE.CuSparseMatrixCSC{Int64})(::CUDA.CUSPARSE.CuSparseMatrixCSR{Int64, Int32})
Closest candidates are:
  (CUDA.CUSPARSE.CuSparseMatrixCSC{T})(::SparseVector) where T at ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/array.jl:347
  (CUDA.CUSPARSE.CuSparseMatrixCSC{T})(::LinearAlgebra.Transpose{T, <:CUDA.CUSPARSE.CuSparseMatrixCSR{T}}) where T at ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/conversions.jl:47
  (CUDA.CUSPARSE.CuSparseMatrixCSC{T})(::SparseMatrixCSC) where T at ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/array.jl:350
  ...
Stacktrace:
  [1] CUDA.CUSPARSE.CuSparseMatrixCSC(x::CUDA.CUSPARSE.CuSparseMatrixCSR{Int64, Int32})
    @ CUDA.CUSPARSE ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/array.jl:365
  [2] CUDA.CUSPARSE.CuSparseMatrixCSC(coo::CUDA.CUSPARSE.CuSparseMatrixCOO{Int64, Int32})
    @ CUDA.CUSPARSE ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/conversions.jl:282
  [3] sparse(I::CuArray{Int32, 1, CUDA.Mem.DeviceBuffer}, J::CuArray{Int32, 1, CUDA.Mem.DeviceBuffer}, V::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, m::Int64, n::Int64; fmt::Symbol)
    @ CUDA.CUSPARSE ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/conversions.jl:34
  [4] sparse(I::CuArray{Int32, 1, CUDA.Mem.DeviceBuffer}, J::CuArray{Int32, 1, CUDA.Mem.DeviceBuffer}, V::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, m::Int64, n::Int64)
    @ CUDA.CUSPARSE ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/conversions.jl:32
  [5] sparse(I::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, J::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, V::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, m::Int64, n::Int64; kws::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ CUDA.CUSPARSE ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/conversions.jl:27
  [6] sparse(I::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, J::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, V::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, m::Int64, n::Int64)
    @ CUDA.CUSPARSE ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/conversions.jl:27
  [7] sparse(I::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, J::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, V::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}; kws::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ CUDA.CUSPARSE ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/conversions.jl:24
  [8] sparse(I::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, J::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}, V::CuArray{Int64, 1, CUDA.Mem.DeviceBuffer})
    @ CUDA.CUSPARSE ~/.julia/packages/CUDA/nRIZ7/lib/cusparse/conversions.jl:24
  [9] top-level scope
    @ REPL[41]:1
 [10] top-level scope
    @ ~/.julia/packages/CUDA/nRIZ7/src/initialization.jl:52
@CarloLucibello CarloLucibello added the bug Something isn't working label Feb 24, 2022
@maleadt
Copy link
Member

maleadt commented Feb 24, 2022

Calling this a bug is a stretch IMO. CUSPARSE.jl mostly aims to wrap the CUSPARSE library, and it doesn't provide CSR to CSC conversions for all types. We could implement it ourselves, as I did with broadcast, but that's generally quite a bit of work.

@CarloLucibello
Copy link
Contributor Author

should we cast to float then? Erroring is pretty annoying. We can add something like

sparse(I::CuVector, J::CuVector, V::CuVector{T}) where T<:Integer = T.(sparse(I, J, Float32.(V))) 

@CarloLucibello
Copy link
Contributor Author

Casting doesn't seem to work

julia> x = sparse([1, 2], [2, 1], [5.0, 5.0])
2×2 SparseMatrixCSC{Float64, Int64} with 2 stored entries:
     5.0
 5.0    

julia> CuSparseMatrixCSR(x)
2×2 CuSparseMatrixCSR{Float64, Int32} with 2 stored entries:
     5.0
 5.0    

julia> Int.(CuSparseMatrixCSR(x))
2×2 CuSparseMatrixCSR{Float64, Int32} with 2 stored entries:
     5.0
 5.0    

julia> CuSparseMatrixCSC(x)
2×2 CuSparseMatrixCSC{Float64, Int32} with 2 stored entries:
     5.0
 5.0    

julia> Int.(CuSparseMatrixCSC(x))
2×2 CuSparseMatrixCSC{Float64, Int32} with 2 stored entries:
     5.0
 5.0    

@maleadt
Copy link
Member

maleadt commented Feb 24, 2022

Let me fix that.

@CarloLucibello
Copy link
Contributor Author

Other data points:

julia> x_int = sparse([1, 2], [2, 1], [5, 5])
2×2 SparseMatrixCSC{Int64, Int64} with 2 stored entries:
   5
 5  

julia> x_int |> CuSparseMatrixCSC # int valued, ok
2×2 CuSparseMatrixCSC{Int64, Int32} with 2 stored entries:
   5
 5  

julia> x_int |> CuSparseMatrixCSC |> CuSparseMatrixCSR
ERROR: MethodError: no method matching (CuSparseMatrixCSR{Int64})(::CuSparseMatrixCSC{Int64, Int32})
Closest candidates are:
  (CuSparseMatrixCSR{T})(::Transpose{Tv, <:SparseMatrixCSC}) where {T, Tv} at ~/.julia/packages/CUDA/BJT41/lib/cusparse/array.jl:353
  (CuSparseMatrixCSR{T})(::Transpose{T, <:CuSparseMatrixCSC{T}}) where T at ~/.julia/packages/CUDA/BJT41/lib/cusparse/conversions.jl:52
  (CuSparseMatrixCSR{T})(::SparseMatrixCSC) where T at ~/.julia/packages/CUDA/BJT41/lib/cusparse/array.jl:359
  ...
Stacktrace:
 [1] CuSparseMatrixCSR(x::CuSparseMatrixCSC{Int64, Int32})
   @ CUDA.CUSPARSE ~/.julia/packages/CUDA/BJT41/lib/cusparse/array.jl:366
 [2] |>(x::CuSparseMatrixCSC{Int64, Int32}, f::Type{CuSparseMatrixCSR})
   @ Base ./operators.jl:966
 [3] top-level scope
   @ REPL[41]:1
 [4] top-level scope
   @ ~/.julia/packages/CUDA/BJT41/src/initialization.jl:52

julia> Float32.(x_int |> CuSparseMatrixCSC) # no cast
2×2 CuSparseMatrixCSC{Int64, Int32} with 2 stored entries:
   5
 5  

julia> x_int |> CuSparseMatrixCSR
ERROR: MethodError: no method matching (CuSparseMatrixCSR{Int64})(::CuSparseMatrixCSC{Int64, Int32})
Closest candidates are:
  (CuSparseMatrixCSR{T})(::Transpose{Tv, <:SparseMatrixCSC}) where {T, Tv} at ~/.julia/packages/CUDA/BJT41/lib/cusparse/array.jl:353
  (CuSparseMatrixCSR{T})(::Transpose{T, <:CuSparseMatrixCSC{T}}) where T at ~/.julia/packages/CUDA/BJT41/lib/cusparse/conversions.jl:52
  (CuSparseMatrixCSR{T})(::SparseMatrixCSC) where T at ~/.julia/packages/CUDA/BJT41/lib/cusparse/array.jl:359
  ...
Stacktrace:
 [1] CuSparseMatrixCSR(x::CuSparseMatrixCSC{Int64, Int32})
   @ CUDA.CUSPARSE ~/.julia/packages/CUDA/BJT41/lib/cusparse/array.jl:366
 [2] (CuSparseMatrixCSR{Int64})(Mat::SparseMatrixCSC{Int64, Int64})
   @ CUDA.CUSPARSE ~/.julia/packages/CUDA/BJT41/lib/cusparse/array.jl:359
 [3] CuSparseMatrixCSR(x::SparseMatrixCSC{Int64, Int64})
   @ CUDA.CUSPARSE ~/.julia/packages/CUDA/BJT41/lib/cusparse/array.jl:366
 [4] |>(x::SparseMatrixCSC{Int64, Int64}, f::Type{CuSparseMatrixCSR})
   @ Base ./operators.jl:966
 [5] top-level scope
   @ REPL[44]:1
 [6] top-level scope
   @ ~/.julia/packages/CUDA/BJT41/src/initialization.jl:52

@maleadt
Copy link
Member

maleadt commented Feb 24, 2022

Yeah I know, that's all the same issue. CUSPARSE doesn't have those constructors, so we don't provide conversion constructors with misleading performance characteristics.

I'm happy to have a look at a generic implementation, but can't find much resources on parallel CSR to/from CSC conversion algorithms.

@CarloLucibello
Copy link
Contributor Author

In my case, I would be happy enough with having just one well-supported type (CSR?) and having
sparse(I::CuVector, J, V), sparse(x::CuMatrix), and cu(x::SparseMatrixCSC) return that (with eltype preserved of course).

@maleadt
Copy link
Member

maleadt commented Feb 24, 2022

one well-supported type

CUSPARSE support for sparse matrix types varies wildly across APIs, and there isn't one good pick for everybody's application. CSR seems the best supported option, but I don't want cu(::CSC) to return a CuCSR. That's just making the call way to magic again, would be significantly breaking, and prevent us from doing The Right Thing once we gain additional native functionality.

I wonder though if we can't just reuse the existing conversion routines while reinterpreting the underlying array (i.e. use the Float32 one for Int32), since they shouldn't look at the data. That would extend the usability a bit, at least.

@CarloLucibello
Copy link
Contributor Author

I don't know if it is the same problem, but this hangs on my system:

julia> using SparseArrays, CUDA

julia> a = sparse([1,2], [1,2], [5,5]) |> cu
2×2 CUDA.CUSPARSE.CuSparseMatrixCSC{Int64, Int32} with 2 stored entries:
 5  
   5

julia> a |> Array # takes forever, have to kill the julia session

@maleadt maleadt changed the title error with sparse(I, J, V) when eltype(V) is integer CUSPARSE does not support integer matrices, breaks printing Feb 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants