From 5982f887c9880b2990ee056f987fe94098d45f2e Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:04:48 +0200 Subject: [PATCH 01/12] Require Adapt 4.7.2 and KernelAbstractions 0.9.43 Adapt 4.7.2 adapts Base.Fix1/Fix2 field-wise. Before, a closure capturing one did not infer when adapted in device code, which is what KernelAbstractions' @Const does, so findfirst/findlast with a Fix2 predicate failed to compile in AcceleratedKernels' reduction kernel on real GPUs. AcceleratedKernels 0.5 already needs KernelAbstractions 0.9.43. --- Project.toml | 4 ++-- src/deprecated.jl | 18 ------------------ 2 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 src/deprecated.jl diff --git a/Project.toml b/Project.toml index e4727be4..dabef720 100644 --- a/Project.toml +++ b/Project.toml @@ -28,10 +28,10 @@ JLD2Ext = "JLD2" [compat] AcceleratedKernels = "0.5" -Adapt = "4.6.1" +Adapt = "4.7.2" GPUArraysCore = "= 0.2.1" JLD2 = "0.4, 0.5, 0.6" -KernelAbstractions = "0.9.28, 0.10" +KernelAbstractions = "0.9.43, 0.10" LLVM = "3.9, 4, 5, 6, 7, 8, 9, 10" LinearAlgebra = "1" Printf = "1" diff --git a/src/deprecated.jl b/src/deprecated.jl deleted file mode 100644 index f8927e23..00000000 --- a/src/deprecated.jl +++ /dev/null @@ -1,18 +0,0 @@ -# RNG: the old RNG took a GPU state array and Vector{UInt32} seeds. -# The new stateless Philox4x32 RNG doesn't need either. - -function RNG(state::AbstractGPUArray) - AT = Base.typename(typeof(state)).wrapper - Base.depwarn("RNG(state::AbstractGPUArray) is deprecated, use RNG{$AT}() instead", :RNG) - RNG{AT}() -end - -function Random.seed!(rng::RNG, seed::Vector{UInt32}) - Base.depwarn("seed!(rng::RNG, seed::Vector{UInt32}) is deprecated, use seed!(rng, seed::Integer) instead", :seed!) - Random.seed!(rng, isempty(seed) ? rand(Random.RandomDevice(), UInt64) : first(seed)) -end - -# Stub kept so downstream packages that still extend `GPUArrays.default_rng` -# (Metal.jl, etc.) continue to load. The interface itself is gone — `RNG{AT}()` -# is now constructed directly — so any extension of this is dead code. -function default_rng end From 4c3ae654fba542015946e359d854ecc19fe5d492 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:04:48 +0200 Subject: [PATCH 02/12] Remove the deprecated RNG methods and the default_rng stub The stub only existed so back-ends extending it kept loading; they need a new release for GPUArrays 12 anyway. --- src/GPUArrays.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/GPUArrays.jl b/src/GPUArrays.jl index 086657c2..d12d6762 100644 --- a/src/GPUArrays.jl +++ b/src/GPUArrays.jl @@ -43,6 +43,5 @@ include("host/statistics.jl") include("host/sparse.jl") include("host/alloc_cache.jl") -include("deprecated.jl") end # module From 9a356697feadf507e0ee9280ee147e2967d2c339 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:04:48 +0200 Subject: [PATCH 03/12] Test scans with an associative, non-commutative operator Composing affine maps is exact and catches swapped operands, including where block aggregates are combined, as in CUDA.jl#3339 for CUDA's own scan. --- test/testsuite/accumulate.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/testsuite/accumulate.jl b/test/testsuite/accumulate.jl index d35a3352..70a567aa 100644 --- a/test/testsuite/accumulate.jl +++ b/test/testsuite/accumulate.jl @@ -61,4 +61,14 @@ # Associative operators need not be commutative @test compare(A -> accumulate((a, b) -> a, A), AT, rand(1:10, 1000)) @test compare(A -> accumulate((a, b) -> a, A; dims=2), AT, rand(1:10, 10, 100)) + # composing affine maps x -> a*x + b is exact and sensitive to operand order, also in the + # combination of block aggregates + compose((a1, b1), (a2, b2)) = (a2 * a1, a2 * b1 + b2) + affine(dims...) = [(rand(-1:1), rand(-3:3)) for _ in CartesianIndices(dims)] + for n in (4, 1_000, 100_000) + @test compare(A -> accumulate(compose, A), AT, affine(n)) + @test compare(A -> accumulate(compose, A; init=(1, 0)), AT, affine(n)) + end + @test compare(A -> accumulate(compose, A; dims=2, init=(1, 0)), AT, affine(7, 10_000)) + @test compare(A -> accumulate(compose, A; dims=1), AT, affine(10_000, 7)) end From 6a925e17c7ba07c45bb712bcbf4f090548ae20db Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:04:48 +0200 Subject: [PATCH 04/12] Release JLArrays as 0.4.0 0.4.0 was never registered, so the release that requires GPUArrays 12 can take that number. --- lib/JLArrays/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/JLArrays/Project.toml b/lib/JLArrays/Project.toml index 10bcd7ac..f91f330f 100644 --- a/lib/JLArrays/Project.toml +++ b/lib/JLArrays/Project.toml @@ -1,6 +1,6 @@ name = "JLArrays" uuid = "27aeb0d3-9eb9-45fb-866b-73c2ecf80fcb" -version = "0.4.1" +version = "0.4.0" authors = ["Tim Besard "] [deps] From 3d25a64182123022a722d3472df6e98ef6f7438e Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:04:48 +0200 Subject: [PATCH 05/12] Document the GPUArrays 12 changes for back-end authors Add a NEWS.md listing what back-ends have to change and may delete, describe what a back-end needs for dense arrays, drop the outdated sortperm requirement for findnz, and fix the links to CUDA.jl's and AMDGPU.jl's array types. --- NEWS.md | 47 +++++++++++++++++++++++++++++++++++++++++++ README.md | 10 +++++++-- docs/src/interface.md | 2 +- 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 NEWS.md diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 00000000..efec3317 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,47 @@ +# GPUArrays.jl release history + +This document lists the noteworthy changes in every breaking release of GPUArrays.jl, +newest first, aimed at the authors of back-end packages. For the complete list of merged +pull requests, see the [GitHub releases](https://github.com/JuliaGPU/GPUArrays.jl/releases). + + +## v12.0 + +Base's reductions, sorting, scans, `findall`, logical indexing and `reverse` are now +implemented once for every `AnyGPUArray`, on top of +[AcceleratedKernels.jl](https://github.com/JuliaGPU/AcceleratedKernels.jl) (AK) 0.5. A +back-end gets all of them by defining nothing beyond the array interface and a +KernelAbstractions back-end, and they follow Base's semantics: stable sorting by default, +`init` applied once, no neutral element needed, Base's result types, empty results and +errors. + +*Breaking changes for back-ends*: + +- The `GPUArrays.mapreducedim!` hook is removed and GPUArrays no longer picks neutral + elements. Every Base reduction goes to AK directly. Back-ends must delete their methods + for the hook, together with the reduction kernels only it used. +- `GPUArrays.default_rng` and the deprecated `RNG(state::AbstractGPUArray)` and + `seed!(rng, ::Vector{UInt32})` methods are removed. Construct the RNG with `RNG{AT}()` + and seed it with an integer. +- KernelAbstractions 0.9.43 or later and Adapt 4.7.2 or later are required. + +*What back-ends should delete*: + +Methods narrower than GPUArrays' keep winning dispatch, so a back-end that keeps its own +`findall`, `to_index`/`to_indices`, `_accumulate!`/`accumulate`, `sort!`/`sortperm!`/ +`partialsort!`, or `reverse!` bypasses GPUArrays' Base conformance, and some of those +methods are ambiguous with GPUArrays' (e.g. `findall(f, ::MyArray)` against +`findall(::Fix2{typeof(in)}, ::AnyGPUArray)`). Delete them. + +A vendor sort or scan (e.g. one built on MPSGraph) can stay as a fast path, as long as it +falls back to GPUArrays' method for the cases it doesn't handle and accepts AK's +algorithm objects (`alg=AK.MergeSort()` etc.). There is no extension point for vendor +reductions; `GPUArrays._ak_mapreducedim!` is internal. + +*Known regressions*: + +- Small reductions along `dims` can be slower than the back-ends' own kernels were (up to + about 4× for a 1000×10 array on CUDA), while large ones are faster. This is tracked in + [AcceleratedKernels.jl#135](https://github.com/JuliaGPU/AcceleratedKernels.jl/issues/135). + +JLArrays 0.4.0 is the matching release of the reference back-end. diff --git a/README.md b/README.md index ca57f3fb..ebd08fbe 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,14 @@ Some (CPU based) examples can be see in the testing library `JLArrays` (located ### Dense array support +Define an array type that subtypes `AbstractGPUArray`, a device-side counterpart with +`Adapt.adapt_structure` rules to convert to it, and a KernelAbstractions back-end returned +by `KernelAbstractions.get_backend`. Broadcasting, `mapreduce` and the other reductions, +sorting, scans, `findall`, logical indexing, `reverse` and most of linear algebra then work +without further methods. See the [interface +documentation](https://juliagpu.github.io/GPUArrays.jl/dev/interface/) and `JLArrays` for a +complete example. + ### Sparse array support (optional) `GPUArrays.jl` provides **device-side** array types for `CSC`, `CSR`, `COO`, and `BSR` matrices, as well as sparse vectors. @@ -54,8 +62,6 @@ For **host-side** types, your custom sparse types should implement: - `csr_type(::Type{T})` - the compressed sparse row type for your backend. A `CuSparseMatrixCSC` would have a `csr_type` of `CuSparseMatrixCSR`. - `coo_type(::Type{T})` - the coordinate sparse matrix type for your backend. A `CuSparseMatrixCSC` would have a `coo_type` of `CuSparseMatrixCOO`. -To use `SparseArrays.findnz`, your host-side type **must** implement `sortperm`. This can be done with scalar indexing, but will be very slow. - Additionally, you need to teach `GPUArrays.jl` how to translate your backend's specific types onto the device. `GPUArrays.jl` provides the device-side types: - `GPUSparseDeviceVector` diff --git a/docs/src/interface.md b/docs/src/interface.md index a9c57c53..514480fc 100644 --- a/docs/src/interface.md +++ b/docs/src/interface.md @@ -29,7 +29,7 @@ struct CustomBackend <: KernelAbstractions.GPU KernelAbstractions.get_backend(a::CA) where CA <: CustomArray = CustomBackend() ``` -There are numerous examples of potential interfaces for GPUArrays, such as with [JLArrays](https://github.com/JuliaGPU/GPUArrays.jl/blob/main/lib/JLArrays/src/JLArrays.jl), [CuArrays](https://github.com/JuliaGPU/CUDA.jl/blob/main/src/gpuarrays.jl), and [ROCArrays](https://github.com/JuliaGPU/AMDGPU.jl/blob/main/src/gpuarrays.jl). +There are numerous examples of potential interfaces for GPUArrays, such as with [JLArrays](https://github.com/JuliaGPU/GPUArrays.jl/blob/main/lib/JLArrays/src/JLArrays.jl), [CuArrays](https://github.com/JuliaGPU/CUDA.jl/blob/main/CUDACore/src/array.jl), and [ROCArrays](https://github.com/JuliaGPU/AMDGPU.jl/blob/main/src/array.jl). ## Caching Allocator From efc96ba2ab63a3db1cffc838ff757ef6eb8e0cb5 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:04:48 +0200 Subject: [PATCH 06/12] Skip back-end tests when the back-end itself fails to install Developing the back-end package was outside the try block, so a back-end whose main branch did not resolve failed the job instead of soft-failing it. Also annotate the GitHub Actions run when OpenCL.jl is skipped, since that job reports success. --- .buildkite/pipeline.yml | 27 +++++++++++---------------- .github/workflows/Test.yml | 9 ++++++--- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index ba2f1593..4770cf47 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -29,10 +29,9 @@ steps: println("--- :julia: Installing CUDA.jl") withenv("JULIA_PKG_PRECOMPILE_AUTO" => 0, "JULIA_PKG_DEVDIR" => devdir) do - Pkg.develop("CUDA") - Pkg.activate(package) - try + Pkg.develop("CUDA") + Pkg.activate(package) Pkg.develop([ PackageSpec(path=gpuarrays), PackageSpec(path=gpuarrayscore), PackageSpec(path=joinpath(package, "CUDACore")), @@ -89,10 +88,9 @@ steps: println("--- :julia: Installing CUDA.jl") withenv("JULIA_PKG_PRECOMPILE_AUTO" => 0, "JULIA_PKG_DEVDIR" => devdir) do - Pkg.develop("CUDA") - Pkg.activate(package) - try + Pkg.develop("CUDA") + Pkg.activate(package) Pkg.develop([PackageSpec(path=gpuarrays), PackageSpec(path=gpuarrayscore)]) TestEnv.activate() catch err @@ -141,10 +139,9 @@ steps: println("--- :julia: Installing AMDGPU.jl") withenv("JULIA_PKG_PRECOMPILE_AUTO" => 0, "JULIA_PKG_DEVDIR" => devdir) do - Pkg.develop("AMDGPU") - Pkg.activate(package) - try + Pkg.develop("AMDGPU") + Pkg.activate(package) Pkg.develop([PackageSpec(path=gpuarrays), PackageSpec(path=gpuarrayscore)]) TestEnv.activate() catch err @@ -195,11 +192,10 @@ steps: println("--- :julia: Installing oneAPI.jl") withenv("JULIA_PKG_PRECOMPILE_AUTO" => 0, "JULIA_PKG_DEVDIR" => devdir) do - Pkg.develop("oneAPI") - include(joinpath(package, "deps", "build_ci.jl")) - Pkg.activate(package) - try + Pkg.develop("oneAPI") + include(joinpath(package, "deps", "build_ci.jl")) + Pkg.activate(package) Pkg.develop([PackageSpec(path=gpuarrays), PackageSpec(path=gpuarrayscore)]) TestEnv.activate() catch err @@ -249,10 +245,9 @@ steps: println("--- :julia: Installing Metal.jl") withenv("JULIA_PKG_PRECOMPILE_AUTO" => 0, "JULIA_PKG_DEVDIR" => devdir) do - Pkg.develop("Metal") - Pkg.activate(package) - try + Pkg.develop("Metal") + Pkg.activate(package) Pkg.develop([PackageSpec(path=gpuarrays), PackageSpec(path=gpuarrayscore)]) TestEnv.activate() catch err diff --git a/.github/workflows/Test.yml b/.github/workflows/Test.yml index e59ba269..5e452b22 100644 --- a/.github/workflows/Test.yml +++ b/.github/workflows/Test.yml @@ -85,14 +85,17 @@ jobs: println("--- :julia: Installing OpenCL.jl") withenv("JULIA_PKG_PRECOMPILE_AUTO" => 0, "JULIA_PKG_DEVDIR" => devdir) do - Pkg.develop("OpenCL") - Pkg.activate(package) - try + Pkg.develop("OpenCL") + Pkg.activate(package) Pkg.develop([PackageSpec(path=gpuarrays), PackageSpec(path=gpuarrayscore)]) TestEnv.activate() catch err + # OpenCL.jl may not support this GPUArrays version yet (e.g. after a + # breaking release): skip, like Buildkite soft-fails, but annotate + # the run so the skip is visible @error "Could not install OpenCL.jl" exception=(err,catch_backtrace()) + println("::warning title=OpenCL.jl not tested::Could not install OpenCL.jl with this version of GPUArrays") exit(0) finally Pkg.activate(package) From 8d89c8f2742707fcd2b20ada6675d765af31eebc Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:35:47 +0200 Subject: [PATCH 07/12] Throw Base's error for sort! with dims < 1 Base throws a BoundsError there (from size(A, dims)), AcceleratedKernels an ArgumentError. Found by AMDGPU.jl's tests, which expect Base's error; the testsuite now compares against Base for out-of-range dims. --- src/host/sorting.jl | 1 + test/testsuite/sorting.jl | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/host/sorting.jl b/src/host/sorting.jl index b8842459..16277bd1 100644 --- a/src/host/sorting.jl +++ b/src/host/sorting.jl @@ -31,6 +31,7 @@ function Base.sort!(v::AnyGPUVector; alg=nothing, lt=isless, by=identity, rev=no return v end function Base.sort!(A::AnyGPUArray; dims::Integer, alg=nothing, scratch=nothing, kwargs...) + size(A, dims) # Base's error for `dims < 1` (AcceleratedKernels throws an ArgumentError) AK.sort!(A; dims, alg=_akalg(alg), kwargs...) return A end diff --git a/test/testsuite/sorting.jl b/test/testsuite/sorting.jl index 48680cf7..2cba872c 100644 --- a/test/testsuite/sorting.jl +++ b/test/testsuite/sorting.jl @@ -63,6 +63,12 @@ end @test_throws UndefKeywordError sort(AT(rand(Float32, 4, 4))) @test_throws UndefKeywordError sort!(AT(rand(Float32, 4, 4))) @test_throws UndefKeywordError sortperm(AT(rand(Float32, 4, 4))) + # Base's errors for a `dims` out of range + for (f, dims) in ((sort!, 0), (sort, 0), (sort!, 3), (sortperm, 0), (sortperm, 3)) + A = rand(Float32, 4, 4) + err = try f(copy(A); dims); nothing catch e; typeof(e) end + @test_throws err f(AT(A); dims) + end end @testsuite "sorting/sortperm" (AT, eltypes)->begin From 2e0ee72aba28991d1c94fe20db3c308e164944b5 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:43:00 +0200 Subject: [PATCH 08/12] Test findfirst and findlast with predicates that have fields findfirst/findlast reduce with a closure capturing the predicate, which KernelAbstractions' @Const adapts in device code. Adapt failed to infer that for captured Fix2 and ComposedFunction predicates (fixed in Adapt 4.7.2 and JuliaGPU/Adapt.jl#124), which JLArrays can't detect but GPU back-ends running the testsuite can. --- test/testsuite/indexing.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/testsuite/indexing.jl b/test/testsuite/indexing.jl index fc404928..f9538948 100644 --- a/test/testsuite/indexing.jl +++ b/test/testsuite/indexing.jl @@ -171,6 +171,12 @@ end # 1D @test compare(findfirst, AT, rand(Bool, 100)) @test compare(x->findfirst(>(0.5f0), x), AT, rand(Float32, 100)) + # predicates with fields, which the kernel's closure captures + let c = 0.5f0 + @test compare(x->findfirst(y -> y > c, x), AT, rand(Float32, 100)) + end + @test compare(x->findfirst(!isnan, x), AT, Float32[NaN, NaN, 1, NaN]) + @test compare(x->findfirst(Base.Fix2(>, 0.5f0) ∘ abs, x), AT, rand(Float32, 100)) @test compare(findfirst, AT, fill(false, 10)) let x = fill(false, 10) @test findfirst(x) == findfirst(AT(x)) @@ -192,6 +198,12 @@ end # 1D @test compare(findlast, AT, rand(Bool, 100)) @test compare(x->findlast(>(0.5f0), x), AT, rand(Float32, 100)) + # predicates with fields, which the kernel's closure captures + let c = 0.5f0 + @test compare(x->findlast(y -> y > c, x), AT, rand(Float32, 100)) + end + @test compare(x->findlast(!isnan, x), AT, Float32[NaN, NaN, 1, NaN]) + @test compare(x->findlast(Base.Fix2(>, 0.5f0) ∘ abs, x), AT, rand(Float32, 100)) let x = fill(false, 10) @test findlast(x) == findlast(AT(x)) end From 6babdf4a559a73c9a41727a0f018fd12d900d8e0 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 07:57:00 +0200 Subject: [PATCH 09/12] Don't require Float64 in the reduction result type test Iris Xe and other devices without Float64 can't run it; a Float32 init tests the same rule. --- test/testsuite/reductions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testsuite/reductions.jl b/test/testsuite/reductions.jl index c69013e1..95fac558 100644 --- a/test/testsuite/reductions.jl +++ b/test/testsuite/reductions.jl @@ -346,7 +346,7 @@ end @test same(f, m8) end # (Base's pairwise path reduces `Int8`s in `Int8`, which the values here do not overflow) - @test same(A -> reduce(+, A; dims, init=0.5), Int16.(m8)) + @test same(A -> reduce(+, A; dims, init=0.5f0), Int16.(m8)) end # ... and empty reduced dimensions: `init`, else Base's initial value or error e8 = zeros(Int8, 0, 3) From 8dc6306a5dcb109c5f0ed8006e3847d58dcda7f8 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 08:15:36 +0200 Subject: [PATCH 10/12] Disambiguate mapreducedim! from adjoint and transposed matrices LinearAlgebra defines Base.mapreducedim! for Adjoint and Transpose sources with specific operators, which was ambiguous with GPUArrays' method for a GPU destination (#521). The extra methods send those calls to AcceleratedKernels like any other source. --- src/host/mapreduce.jl | 12 ++++++++++++ test/testsuite/reductions.jl | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/src/host/mapreduce.jl b/src/host/mapreduce.jl index 6ce7fe48..d14ac37d 100644 --- a/src/host/mapreduce.jl +++ b/src/host/mapreduce.jl @@ -19,6 +19,18 @@ Base.mapreducedim!(f, op, R::AnyGPUArray, A::AbstractArray) = _ak_mapreducedim!(f, op, R, A; backend=get_backend(R)) Base.mapreducedim!(f, op, R::AnyGPUArray, A::Broadcast.Broadcasted) = _ak_mapreducedim!(f, op, R, A; backend=get_backend(R)) +# (LinearAlgebra's methods for adjoint and transposed matrices are ambiguous with these) +for W in (:Adjoint, :Transpose) + @eval begin + Base.mapreducedim!(f, op::Union{typeof(&), typeof(|), typeof(+), typeof(Base.add_sum), + typeof(max), typeof(min), typeof(Base._extrema_rf)}, + R::AnyGPUArray, A::$W{<:Any, <:AbstractMatrix}) = + _ak_mapreducedim!(f, op, R, A; backend=get_backend(R)) + Base.mapreducedim!(f::typeof(identity), op::Union{typeof(*), typeof(Base.mul_prod)}, + R::AnyGPUArray, A::$W{<:Union{Real, Complex}, <:AbstractMatrix}) = + _ak_mapreducedim!(f, op, R, A; backend=get_backend(R)) + end +end # `neutral_element` lives in GPUArraysCore, so that packages building on GPUArraysCore share it import GPUArraysCore: neutral_element diff --git a/test/testsuite/reductions.jl b/test/testsuite/reductions.jl index 95fac558..b9f258dd 100644 --- a/test/testsuite/reductions.jl +++ b/test/testsuite/reductions.jl @@ -39,6 +39,10 @@ end for t in [transpose, adjoint] @test compare((A,R)->Base.mapreducedim!(identity, +, R, A), AT, rand(range, (2,2)), t(zeros(ET, (2,)))) @test compare((A,R)->Base.mapreducedim!(abs2, +, R, A), AT, rand(range, (3,2,10)), t(zeros(ET, (2,3)))) + # ... and from them (LinearAlgebra has methods for these sources) + @test compare((A,R)->Base.mapreducedim!(identity, +, R, t(A)), AT, rand(range, (3,4)), zeros(ET, (4,))) + @test compare((A,R)->Base.mapreducedim!(abs2, max, R, t(A)), AT, rand(range, (3,4)), zeros(real(ET), (1,3))) + @test compare((A,R)->Base.mapreducedim!(identity, *, R, t(A)), AT, rand(range, (3,4)), ones(ET, (4,))) # TODO: reenable once https://github.com/JuliaGPU/Metal.jl/issues/907 is fixed # @test compare((A,R)->sum!(abs2, R, A), AT, rand(range, (3,2,10)), t(zeros(ET, (2,3)))) From 77c17f58dc8ed0ec7edc97353a1dd93cc3b605ba Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 09:35:07 +0200 Subject: [PATCH 11/12] Require Adapt 4.7.3 Adapt 4.7.3 also adapts closures capturing a ComposedFunction (e.g. a !f predicate, as in cholesky(::Diagonal)) inferably, and on Julia 1.10 closures capturing closures. --- NEWS.md | 2 +- Project.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index efec3317..9c909aec 100644 --- a/NEWS.md +++ b/NEWS.md @@ -23,7 +23,7 @@ errors. - `GPUArrays.default_rng` and the deprecated `RNG(state::AbstractGPUArray)` and `seed!(rng, ::Vector{UInt32})` methods are removed. Construct the RNG with `RNG{AT}()` and seed it with an integer. -- KernelAbstractions 0.9.43 or later and Adapt 4.7.2 or later are required. +- KernelAbstractions 0.9.43 or later and Adapt 4.7.3 or later are required. *What back-ends should delete*: diff --git a/Project.toml b/Project.toml index dabef720..10e73faf 100644 --- a/Project.toml +++ b/Project.toml @@ -28,7 +28,7 @@ JLD2Ext = "JLD2" [compat] AcceleratedKernels = "0.5" -Adapt = "4.7.2" +Adapt = "4.7.3" GPUArraysCore = "= 0.2.1" JLD2 = "0.4, 0.5, 0.6" KernelAbstractions = "0.9.43, 0.10" From 69dd9f1c587ba5681fd3281571665bd3ef50c857 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 7 Oct 2026 09:44:56 +0200 Subject: [PATCH 12/12] Throw AcceleratedKernels' ArgumentError for sort! with dims < 1 Base's error there is incidental and differs between versions (a BoundsError from size(A, 0), or an ErrorException from arraysize), so matching it is not worth an extra check; an ArgumentError is what out-of-range dims give everywhere else (sortperm, dims > ndims). Test only those consistent cases. --- src/host/sorting.jl | 1 - test/testsuite/sorting.jl | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/host/sorting.jl b/src/host/sorting.jl index 16277bd1..b8842459 100644 --- a/src/host/sorting.jl +++ b/src/host/sorting.jl @@ -31,7 +31,6 @@ function Base.sort!(v::AnyGPUVector; alg=nothing, lt=isless, by=identity, rev=no return v end function Base.sort!(A::AnyGPUArray; dims::Integer, alg=nothing, scratch=nothing, kwargs...) - size(A, dims) # Base's error for `dims < 1` (AcceleratedKernels throws an ArgumentError) AK.sort!(A; dims, alg=_akalg(alg), kwargs...) return A end diff --git a/test/testsuite/sorting.jl b/test/testsuite/sorting.jl index 2cba872c..f288d1e7 100644 --- a/test/testsuite/sorting.jl +++ b/test/testsuite/sorting.jl @@ -63,11 +63,9 @@ end @test_throws UndefKeywordError sort(AT(rand(Float32, 4, 4))) @test_throws UndefKeywordError sort!(AT(rand(Float32, 4, 4))) @test_throws UndefKeywordError sortperm(AT(rand(Float32, 4, 4))) - # Base's errors for a `dims` out of range - for (f, dims) in ((sort!, 0), (sort, 0), (sort!, 3), (sortperm, 0), (sortperm, 3)) - A = rand(Float32, 4, 4) - err = try f(copy(A); dims); nothing catch e; typeof(e) end - @test_throws err f(AT(A); dims) + # a `dims` out of range (Base's error for `sort(A; dims=0)` differs between versions) + for (f, dims) in ((sort!, 3), (sort, 3), (sortperm, 0), (sortperm, 3)) + @test_throws ArgumentError f(AT(rand(Float32, 4, 4)); dims) end end