From 21093c8b46412a13f43e5e3d98128c6f5cc1cadf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Sat, 7 Nov 2026 03:16:30 +0100 Subject: [PATCH] Restore non-differentiable `rand!(::AbstractRNG, ::AbstractArray)` GPUArrays and CUDA implement `rand!(rng, A)` directly, without a sampler, and forward their out-of-place `rand(rng, ...)` methods to it. Since #855 removed this rule, Zygote differentiates into their kernels and errors. Co-Authored-By: Claude Opus 5.5 --- Project.toml | 2 +- src/rulesets/Random/random.jl | 2 ++ test/rulesets/Random/random.jl | 21 ++++++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 65c7177b9..277f12aeb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ChainRules" uuid = "082447d4-558c-5d27-93f4-14fc19e9eca2" -version = "1.74.11" +version = "1.74.12" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" diff --git a/src/rulesets/Random/random.jl b/src/rulesets/Random/random.jl index ef9f67b0d..1fe4dd803 100644 --- a/src/rulesets/Random/random.jl +++ b/src/rulesets/Random/random.jl @@ -26,6 +26,8 @@ const NonDifferentiableSampler = Union{ } @non_differentiable rand(::AbstractRNG, ::NonDifferentiableSampler) @non_differentiable rand!(::AbstractRNG, ::AbstractArray, ::NonDifferentiableSampler) +# GPUArrays and CUDA implement this directly, without a sampler +@non_differentiable rand!(::AbstractRNG, ::AbstractArray) @non_differentiable randexp(::Any...) diff --git a/test/rulesets/Random/random.jl b/test/rulesets/Random/random.jl index dbff1c824..49cb707d7 100644 --- a/test/rulesets/Random/random.jl +++ b/test/rulesets/Random/random.jl @@ -1,4 +1,4 @@ -using ChainRules, ChainRulesCore, Random, Test +using ChainRules, ChainRulesCore, JLArrays, Random, Test # Simple Distributions like object for testing purposes struct NormalDistribution @@ -7,6 +7,10 @@ struct NormalDistribution end Random.rand(d::NormalDistribution) = d.μ + d.σ*randn() +# RNG that, like GPUArrays and CUDA, implements `rand!(rng, A)` without a sampler +struct DirectRNG <: AbstractRNG end +Random.rand!(::DirectRNG, A::Array) = fill!(A, 0.5) + @testset "Random" begin rng_types = [MersenneTwister, Xoshiro] @@ -121,4 +125,19 @@ Random.rand(d::NormalDistribution) = d.μ + d.σ*randn() end @test rrule(rand, rng, Random.SamplerRangeNDL(1.5, UInt64(4))) === nothing end + + @testset "rand!(rng, A)" begin + for (rng, A) in ( + (Xoshiro(123), zeros(3)), + (Xoshiro(123), JLArray(zeros(Float32, 3))), # GPUArrays: no sampler + (DirectRNG(), zeros(3)), + ) + Ω, dΩ = frule((NoTangent(), NoTangent(), NoTangent()), rand!, rng, A) + @test Ω === A + @test dΩ isa NoTangent + Ω, pb! = rrule(rand!, rng, A) + @test Ω === A + @test all(darg isa NoTangent for darg in pb!(ones(3))) + end + end end