Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ Distributions = "0.25"
ExplicitImports = "1.14.0"
JET = "0.9, 0.10, 0.11"
LogExpFunctions = "0.3.14, 1.0"
Pkg = "1"
PrecompileTools = "1.2.0"
Random = "1.10"
SafeTestsets = "0.1, 1"
SciMLTesting = "1"
Statistics = "1"
Test = "1"
julia = "1.10"
Expand All @@ -27,9 +28,10 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["AllocCheck", "Aqua", "Statistics", "Test", "Distributions", "JET", "ExplicitImports", "Pkg"]
test = ["AllocCheck", "Aqua", "Statistics", "Test", "Distributions", "JET", "ExplicitImports", "SafeTestsets", "SciMLTesting"]
25 changes: 25 additions & 0 deletions test/bigfloat_tests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using PoissonRandom
using Statistics
using Test

@testset "count_rand with BigFloat (λ < 6)" begin
for _ in 1:100
result = pois_rand(BigFloat(3.0))
@test result isa Integer
@test result >= 0
end
end
@testset "ad_rand with BigFloat (λ >= 6)" begin
for _ in 1:100
result = pois_rand(BigFloat(15.0))
@test result isa Integer
@test result >= 0
end
end
@testset "statistical validity with BigFloat" begin
n = 10000
λ = BigFloat(10.0)
samples = [pois_rand(λ) for _ in 1:n]
sample_mean = mean(samples)
@test abs(sample_mean - Float64(λ)) < 3 * sqrt(Float64(λ))
end
16 changes: 16 additions & 0 deletions test/passthrough_rng_tests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using PoissonRandom
using Random: Random, UInt52Raw
using Test

prng = PassthroughRNG()
# The CUDA.jl @device_override Random.randexp(::AbstractRNG) shadows our
# specific Random.randexp(::PassthroughRNG) on the GPU because Julia's
# OverlayMethodTable returns overlay matches without consulting the base
# table when the overlay fully covers the signature. The override body
# then calls these against PassthroughRNG; if they MethodError, kernel
# compilation fails with InvalidIRError on jl_f_throw_methoderror.
@test Random.rng_native_52(prng) === UInt64
@test Random.rand(prng, UInt52Raw()) isa UInt64
@test Random.rand(prng, UInt64) isa UInt64
@test Random.rand(prng, Float32) isa Float32
@test Random.rand(prng, Float64) isa Float64
4 changes: 4 additions & 0 deletions test/qa/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
PoissonRandom = "e409e4f3-bfea-5376-8464-e040bb5c01ab"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
SciMLTesting = "09d9d899-5365-40a9-917a-5f67fddea283"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[sources]
Expand All @@ -14,5 +16,7 @@ Aqua = "0.8"
ExplicitImports = "1.14.0"
JET = "0.9, 0.10, 0.11"
Random = "1.10"
SafeTestsets = "0.1, 1"
SciMLTesting = "1"
Test = "1"
julia = "1.10"
147 changes: 2 additions & 145 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,145 +1,2 @@
using PoissonRandom
import Distributions
using Test, Statistics

const GROUP = get(ENV, "GROUP", "All")

if GROUP == "QA"
using Pkg
Pkg.activate(joinpath(@__DIR__, "qa"))
Pkg.develop(Pkg.PackageSpec(path = joinpath(@__DIR__, "..")))
Pkg.instantiate()
include(joinpath(@__DIR__, "qa", "qa.jl"))
else
n_tsamples = 10^5

function test_samples(
rand_func,
distr::Distributions.DiscreteUnivariateDistribution,
n::Int; # number of samples to generate
q::Float64 = 1.0e-8, # confidence interval, 1 - q as confidence
verbose::Bool = false
) # show intermediate info (for debugging)

# The basic idea
# ------------------
# Generate n samples, and count the occurrences of each value within a reasonable range.
# For each distinct value, it computes an confidence interval of the counts
# and checks whether the count is within this interval.
#
# If the distribution has a bounded range, it also checks whether
# the samples are all within this range.
#
# By setting a small q, we ensure that failure of the tests rarely
# happen in practice.
#
λ = distr.λ
n > 1 || error("The number of samples must be greater than 1.")
0.0 < q < 0.1 || error("The value of q must be within the open interval (0.0, 0.1).")

# determine the range of values to examine
vmin = minimum(distr)
vmax = maximum(distr)

rmin = floor(Int, quantile(distr, 0.00001))::Int
rmax = floor(Int, quantile(distr, 0.99999))::Int
m = rmax - rmin + 1 # length of the range
p0 = Distributions.pdf.((distr,), rmin:rmax) # reference probability masses
@assert length(p0) == m

# determine confidence intervals for counts:
# with probability q, the count will be out of this interval.
#
clb = Vector{Int}(undef, m)
cub = Vector{Int}(undef, m)
for i in 1:m
bp = Distributions.Binomial(n, p0[i])
clb[i] = floor(Int, quantile(bp, q / 2))
cub[i] = ceil(Int, Distributions.cquantile(bp, q / 2))
@assert cub[i] >= clb[i]
end

# generate samples
samples = [rand_func(λ) for i in 1:n]
@assert length(samples) == n

# scan samples and get counts
cnts = zeros(Int, m)
for i in 1:n
@inbounds si = samples[i]
if rmin <= si <= rmax
cnts[si - rmin + 1] += 1
else
vmin <= si <= vmax ||
error("Sample value out of valid range.")
end
end

# check the counts
for i in 1:m
verbose && println("v = $(rmin + i - 1) ==> ($(clb[i]), $(cub[i])): $(cnts[i])")
clb[i] <= cnts[i] <= cub[i] ||
error("The counts are out of the confidence interval.")
end
return samples
end

println("testing count random sampler")
for λ in [0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0, 30.0]
test_samples(PoissonRandom.count_rand, Distributions.Poisson(λ), n_tsamples)
end

println("testing ad random sampler")
for λ in [5.0, 10.0, 15.0, 20.0, 30.0]
test_samples(PoissonRandom.ad_rand, Distributions.Poisson(λ), n_tsamples)
end

println("testing mixed random sampler")
for λ in [5.0, 10.0, 15.0, 20.0, 30.0]
test_samples(pois_rand, Distributions.Poisson(λ), n_tsamples)
end

@testset "BigFloat support" begin
@testset "count_rand with BigFloat (λ < 6)" begin
for _ in 1:100
result = pois_rand(BigFloat(3.0))
@test result isa Integer
@test result >= 0
end
end
@testset "ad_rand with BigFloat (λ >= 6)" begin
for _ in 1:100
result = pois_rand(BigFloat(15.0))
@test result isa Integer
@test result >= 0
end
end
@testset "statistical validity with BigFloat" begin
n = 10000
λ = BigFloat(10.0)
samples = [pois_rand(λ) for _ in 1:n]
sample_mean = mean(samples)
@test abs(sample_mean - Float64(λ)) < 3 * sqrt(Float64(λ))
end
end

@testset "PassthroughRNG dispatch" begin
using Random: Random, UInt52Raw
prng = PassthroughRNG()
# The CUDA.jl @device_override Random.randexp(::AbstractRNG) shadows our
# specific Random.randexp(::PassthroughRNG) on the GPU because Julia's
# OverlayMethodTable returns overlay matches without consulting the base
# table when the overlay fully covers the signature. The override body
# then calls these against PassthroughRNG; if they MethodError, kernel
# compilation fails with InvalidIRError on jl_f_throw_methoderror.
@test Random.rng_native_52(prng) === UInt64
@test Random.rand(prng, UInt52Raw()) isa UInt64
@test Random.rand(prng, UInt64) isa UInt64
@test Random.rand(prng, Float32) isa Float32
@test Random.rand(prng, Float64) isa Float64
end

@testset "Allocation Tests" begin
include("alloc_tests.jl")
end
end
using SciMLTesting
run_tests()
91 changes: 91 additions & 0 deletions test/sampler_tests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using PoissonRandom
import Distributions
using Statistics

n_tsamples = 10^5

function test_samples(
rand_func,
distr::Distributions.DiscreteUnivariateDistribution,
n::Int; # number of samples to generate
q::Float64 = 1.0e-8, # confidence interval, 1 - q as confidence
verbose::Bool = false
) # show intermediate info (for debugging)

# The basic idea
# ------------------
# Generate n samples, and count the occurrences of each value within a reasonable range.
# For each distinct value, it computes an confidence interval of the counts
# and checks whether the count is within this interval.
#
# If the distribution has a bounded range, it also checks whether
# the samples are all within this range.
#
# By setting a small q, we ensure that failure of the tests rarely
# happen in practice.
#
λ = distr.λ
n > 1 || error("The number of samples must be greater than 1.")
0.0 < q < 0.1 || error("The value of q must be within the open interval (0.0, 0.1).")

# determine the range of values to examine
vmin = minimum(distr)
vmax = maximum(distr)

rmin = floor(Int, quantile(distr, 0.00001))::Int
rmax = floor(Int, quantile(distr, 0.99999))::Int
m = rmax - rmin + 1 # length of the range
p0 = Distributions.pdf.((distr,), rmin:rmax) # reference probability masses
@assert length(p0) == m

# determine confidence intervals for counts:
# with probability q, the count will be out of this interval.
#
clb = Vector{Int}(undef, m)
cub = Vector{Int}(undef, m)
for i in 1:m
bp = Distributions.Binomial(n, p0[i])
clb[i] = floor(Int, quantile(bp, q / 2))
cub[i] = ceil(Int, Distributions.cquantile(bp, q / 2))
@assert cub[i] >= clb[i]
end

# generate samples
samples = [rand_func(λ) for i in 1:n]
@assert length(samples) == n

# scan samples and get counts
cnts = zeros(Int, m)
for i in 1:n
@inbounds si = samples[i]
if rmin <= si <= rmax
cnts[si - rmin + 1] += 1
else
vmin <= si <= vmax ||
error("Sample value out of valid range.")
end
end

# check the counts
for i in 1:m
verbose && println("v = $(rmin + i - 1) ==> ($(clb[i]), $(cub[i])): $(cnts[i])")
clb[i] <= cnts[i] <= cub[i] ||
error("The counts are out of the confidence interval.")
end
return samples
end

println("testing count random sampler")
for λ in [0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0, 30.0]
test_samples(PoissonRandom.count_rand, Distributions.Poisson(λ), n_tsamples)
end

println("testing ad random sampler")
for λ in [5.0, 10.0, 15.0, 20.0, 30.0]
test_samples(PoissonRandom.ad_rand, Distributions.Poisson(λ), n_tsamples)
end

println("testing mixed random sampler")
for λ in [5.0, 10.0, 15.0, 20.0, 30.0]
test_samples(pois_rand, Distributions.Poisson(λ), n_tsamples)
end
Loading