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

Broadcasted setindex! triggers scalar setindex! #101

Closed
ChrisRackauckas opened this issue Jan 25, 2020 · 19 comments · Fixed by JuliaGPU/CuArrays.jl#581
Closed

Broadcasted setindex! triggers scalar setindex! #101

ChrisRackauckas opened this issue Jan 25, 2020 · 19 comments · Fixed by JuliaGPU/CuArrays.jl#581
Labels
cuda array Stuff about CuArray. enhancement New feature or request

Comments

@ChrisRackauckas
Copy link
Member

using CuArrays
CuArrays.allowscalar(false)
J = cu(zeros(30,30))
rows_index, cols_index, colorvec, cols_index, color_i = (1:30, 1:30, 1:30, 1:30, 1)
vecdx = cu(Float32[-2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
@. setindex!((J,),getindex((J,),rows_index, cols_index) + (getindex((colorvec,),cols_index) == color_i) * getindex((vecdx,),rows_index),rows_index, cols_index)
@ChrisRackauckas
Copy link
Member Author

ChrisRackauckas commented Jan 25, 2020

Looks like it can be even simpler:

using CuArrays
CuArrays.allowscalar(false)
J = cu(zeros(30,30))
setindex!.((J,),1.0,1:30,1:30)

@ChrisRackauckas ChrisRackauckas changed the title Scalar getindex triggered in a broadcast expression Broadcasted setindex! triggers scalar setindex! Jan 25, 2020
@maleadt
Copy link
Member

maleadt commented Jan 30, 2020

Yes, this is expected. Broadcast descends into all arguments, including the 1:30 ranges, which means you end up calling setindex!(J, 1.0, X, Y) where X and Y are scalars. The very definition of scalar indexing.

If you prevent broadcast from expanding the ranges, everything works:

julia> setindex!.((J,),2.0,Ref(1:30),Ref(1:30))
(Float32[2.0 2.0 … 2.0 2.0; 2.0 2.0 … 2.0 2.0; … ; 2.0 2.0 … 2.0 2.0; 2.0 2.0 … 2.0 2.0],)

julia> J
30×30 CuArray{Float32,2,Nothing}:
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0
 2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0  2.0

The outer broadcast itself can't be executed on the GPU because you're essentially doing a host-side broadcast on a tuple of arrays.

@maleadt maleadt closed this as completed Jan 30, 2020
@ChrisRackauckas
Copy link
Member Author

Broadcast descends into all arguments, including the 1:30 ranges, which means you end up calling setindex!(J, 1.0, X, Y) where X and Y are scalars. The very definition of scalar indexing.

But it's a broadcast expression, so theoretically this could've built a kernel instead of 1 by 1?

setindex!.((J,),x,Ref(1:30),Ref(1:30)) won't work when x is an array though? So is this kind of operation just impossible to do generically on the GPU, or you have to write a generated function of sorts?

@maleadt
Copy link
Member

maleadt commented Jan 30, 2020

setindex!.((J,),x,Ref(1:30),Ref(1:30)) won't work when x is an array though?

But it doesn't on the CPU either?

julia> setindex!.((zeros(30,30),),1:30,1:30,1:30)
30-element Array{Array{Float64,2},1}:
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]
 [1.0 0.0 … 0.0 0.0; 0.0 2.0 … 0.0 0.0; … ; 0.0 0.0 … 29.0 0.0; 0.0 0.0 … 0.0 30.0]

@ChrisRackauckas
Copy link
Member Author

I guess I simplified https://github.com/JuliaDiff/SparseDiffTools.jl/blob/master/src/differentiation/compute_jacobian_ad.jl#L192 a little bit too much. But that will still scalar index because it's broadcasting get/set index?

@maleadt
Copy link
Member

maleadt commented Jan 30, 2020

Ah, looking at that code I understand what you're getting at. That tuple threw me off, why don't you just setindex!.(Ref(J),1.0,1:30,1:30)? The (J,) seems like a weird hack to bypass broadcast.

@maleadt
Copy link
Member

maleadt commented Jan 30, 2020

I think this is fixable. Will have a look tomorrow.

@maleadt maleadt reopened this Jan 30, 2020
@maleadt
Copy link
Member

maleadt commented Jan 31, 2020

I'm not going to be able to fix this completely, because setindex! apparently returns the original array:

julia> setindex!(J,1.0,1,1);

julia> typeof(ans)
Array{Float64,2}

Causing the broadcast to return an array of arrays:

julia> setindex!.(Ref(J),1.0,1:30,1:30);

julia> typeof(ans)
Array{Array{Float64,2},1}

(even if the 0D broadcast were to unwrap, which I thought it should, it would need to save the output of that one setindex! somewhere leading to the very same problem)

Can make this work by locally defining a void_setindex!(args...) = setindex!(args...), would that be OK? Or any other suggestions?

@ChrisRackauckas
Copy link
Member Author

Can make this work by locally defining a void_setindex!(args...) = setindex!(args...), would that be OK? Or any other suggestions?

That works for me. Would we have to locally use void_setindex! or would that just be an internal thing? I didn't even know setindex! was supposed to have a return!

@maleadt
Copy link
Member

maleadt commented Jan 31, 2020

I didn't even know setindex! was supposed to have a return!

Neither did I, especially since assignment has different semantics:

julia> a = [1]
1-element Array{Int64,1}:
 1

julia> typeof(begin a[1] = 2 end)
Int64

julia> typeof(setindex!(a, 2, 1))
Array{Int64,1}

julia> Meta.@lower a[1] = 2
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─     Base.setindex!(a, 2, 1)
└──     return 2
))))

You'd have to define a void_setindex! locally yourself.

@ChrisRackauckas
Copy link
Member Author

I'll just add void_setindex! to ArrayInterface.jl and try to use it more pervasively. I assume you meant:

void_setindex!(args...) = (setindex!(args...); nothing)

?

@maleadt
Copy link
Member

maleadt commented Jan 31, 2020

Yes, but wait a while before finalizing on that, I'm still in the process of fixing the GPUArrays broadcast machinery to make this possible in the first place.

@maleadt
Copy link
Member

maleadt commented Feb 3, 2020

The following works now:

    a = [1]
    jl = JLArray(a)
    cu = CuArray(a)

    void(f) = (args...)->(f(args...); return)

    void(setindex!).(Ref(a),1)
    void(setindex!).(Ref(jl),1)
    void(setindex!).(Ref(cu),1)

But you need GPUArrays/CuArrays/CUDAnative from master.

ChrisRackauckas referenced this issue in JuliaDiff/SparseDiffTools.jl Feb 3, 2020
@maleadt
Copy link
Member

maleadt commented Feb 4, 2020

Might still be worth some feedback from broadcast experts though, maybe cc @mbauman? Specifically, https://github.com/JuliaGPU/CuArrays.jl/issues/571#issuecomment-580776718, where returning an array of arrays breaks GPU support. Any way to avoid that with the current design, or suggestions on how to make that possible?

@ChrisRackauckas
Copy link
Member Author

Looks like this is fine on Ref, but breaks when using tuples to stop the broadcast:

using CuArrays, GPUArrays
a = [1]
A = CuArray(a)

void(f) = (args...)->(f(args...); return)
@. void(setindex!).(Ref(a),1)
@. void(setindex!).(Ref(A),1)
@. void(setindex!).((a,),1)
@. void(setindex!).((A,),1)

Note that tuples are recommended: JuliaLang/julia#35591

And the actual application seems to not work: JuliaDiff/SparseDiffTools.jl#104

@maleadt maleadt reopened this May 1, 2020
@maleadt maleadt transferred this issue from JuliaGPU/CuArrays.jl May 27, 2020
@maleadt maleadt added cuda array Stuff about CuArray. enhancement New feature or request labels May 27, 2020
@mbauman
Copy link
Contributor

mbauman commented Feb 11, 2022

So the reason that tuples aren't working is that CUDA isn't grabbing tuples of CuArrays as being a CuArrayStyle:

julia> typeof(Broadcast.broadcasted(void(setindex!), Ref(A), 1))
Base.Broadcast.Broadcasted{CUDA.CuArrayStyle{0}, Nothing, var"#1#2"{typeof(setindex!)}, Tuple{Base.RefValue{CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}}, Int64}}

julia> typeof(Broadcast.broadcasted(void(setindex!), (A,), 1))
Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple}, Nothing, var"#1#2"{typeof(setindex!)}, Tuple{Tuple{CuArray{Int64, 1, CUDA.Mem.DeviceBuffer}}, Int64}}

I'm not sure off-hand what the magic would be to ensure that Tuple{<:CuArray} gets flagged as a CuArrayStyle, but that'll do the ticket here. It's a little tricky because we typically only look at the outermost containers in determining what the array style should be.

@ChrisRackauckas
Copy link
Member Author

Why does Ref work then?

@maleadt
Copy link
Member

maleadt commented Feb 14, 2022

Why does Ref work then?

Because we special-case it here:

https://github.com/JuliaGPU/GPUArrays.jl/blob/caff08e88cd0579c5cfa7faa4d8e1e5dad8c153c/src/host/broadcast.jl#L25-L29

So the reason that tuples aren't working is that CUDA isn't grabbing tuples of CuArrays as being a CuArrayStyle:

Right, but we don't want that or the following starts to return a device array:

julia> BroadcastStyle(::Type{Tuple{AT}}) where {AT<:AbstractGPUArray} = typeof(BroadcastStyle(AT))(Val(1))

julia> julia> size.((CUDA.rand(1),))
1-element CuArray{Tuple{Int64}, 1, CUDA.Mem.DeviceBuffer}:
 (1,)

@ChrisRackauckas
Copy link
Member Author

Looks like this has adequate solutions now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cuda array Stuff about CuArray. enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants