Skip to content

Commit cb43e2a

Browse files
committed
Update validation tests to both use w1 distance and a more logical tolerance
1 parent effb115 commit cb43e2a

File tree

1 file changed

+181
-113
lines changed

1 file changed

+181
-113
lines changed

test/riemannian.jl

Lines changed: 181 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -347,143 +347,211 @@ end
347347
#### Validation tests
348348
####
349349

350-
@testset "Validation testing (Gaussian)" begin
351-
target = HighDimGaussian(2)
352-
rng = MersenneTwister(125)
353-
λ = 1e-2
350+
@testset "Validation testing" begin
351+
# 1D Wasserstein-1 distance
352+
function w1(a::AbstractVector, b::AbstractVector)
353+
sa = sort(a)
354+
sb = sort(b)
355+
return mean(abs.(sa .- sb))
356+
end
354357

355-
initial_θ = rand(rng, dim(target))
358+
@testset "Validation testing (Gaussian)" begin
356359

357-
ℓπ = MCMCLogDensityProblems.gen_logpdf(target)
358-
∂ℓπ∂θ = MCMCLogDensityProblems.gen_logpdf_grad(target, initial_θ)
360+
# 1D normal Wasserstein-1 distance tolerance estimator
361+
function w1_tol_normal_1d(;
362+
n::Int, reps::Int=200, q::Float64=0.999, rng=Random.default_rng()
363+
)
364+
poolN = max(50_000, 50n)
365+
pool = randn(rng, poolN)
366+
367+
vals = Vector{Float64}(undef, reps)
368+
for i in 1:reps
369+
a = pool[rand(rng, 1:poolN, n)]
370+
b = pool[rand(rng, 1:poolN, n)]
371+
vals[i] = w1(a, b)
372+
end
373+
sort!(vals)
374+
return vals[clamp(ceil(Int, q * reps), 1, reps)]
375+
end
359376

360-
_, _, G, ∂G∂θ = prepare_sample(ℓπ, initial_θ, λ)
377+
target = HighDimGaussian(2)
378+
rng = MersenneTwister(125)
379+
λ = 1e-2
361380

362-
D = dim(target)
381+
initial_θ = rand(rng, dim(target))
363382

364-
n_samples = 100
365-
n_adapts = 50
383+
ℓπ = MCMCLogDensityProblems.gen_logpdf(target)
384+
∂ℓπ∂θ = MCMCLogDensityProblems.gen_logpdf_grad(target, initial_θ)
366385

367-
mean_tol = 3 / sqrt(n_samples)
368-
var_tol = 1.5 * sqrt(2 / (n_samples - 1))
386+
_, _, G, ∂G∂θ = prepare_sample(ℓπ, initial_θ, λ)
369387

370-
@testset "RiemannianMetric (PDMat-style)" begin
371-
metric = RiemannianMetric((D,), G, ∂G∂θ)
372-
kinetic = GaussianKinetic()
373-
hamiltonian = Hamiltonian(metric, kinetic, ℓπ, ∂ℓπ∂θ)
388+
D = dim(target)
374389

375-
initial_ϵ = 0.01
376-
integrator = GeneralizedLeapfrog(initial_ϵ, 15)
377-
kernel = HMCKernel(Trajectory{MultinomialTS}(integrator, GeneralisedNoUTurn()))
378-
379-
acceptance_rate = 0.9
380-
adaptor = StepSizeAdaptor(acceptance_rate, integrator)
381-
382-
samples, stats = sample(
383-
rng,
384-
hamiltonian,
385-
kernel,
386-
initial_θ,
387-
n_samples,
388-
adaptor,
389-
n_adapts;
390-
progress=false,
391-
)
392-
@test mean(samples) zeros(D) atol = mean_tol
393-
@test Statistics.var(samples) ones(D) atol = var_tol
390+
n_samples = 100
391+
n_adapts = 50
392+
393+
tol_w1 = w1_tol_normal_1d(; n=n_samples, rng=rng)
394+
395+
# Samples are RHMC so we relax the tolerance
396+
tol_w1 *= 2.0
397+
398+
x_true = randn(rng, n_samples)
399+
y_true = randn(rng, n_samples)
400+
401+
@testset "RiemannianMetric (PDMat-style)" begin
402+
metric = RiemannianMetric((D,), G, ∂G∂θ)
403+
kinetic = GaussianKinetic()
404+
hamiltonian = Hamiltonian(metric, kinetic, ℓπ, ∂ℓπ∂θ)
405+
406+
initial_ϵ = 0.01
407+
integrator = GeneralizedLeapfrog(initial_ϵ, 15)
408+
kernel = HMCKernel(Trajectory{MultinomialTS}(integrator, GeneralisedNoUTurn()))
409+
410+
acceptance_rate = 0.9
411+
adaptor = StepSizeAdaptor(acceptance_rate, integrator)
412+
413+
samples, stats = sample(
414+
rng,
415+
hamiltonian,
416+
kernel,
417+
initial_θ,
418+
n_samples,
419+
adaptor,
420+
n_adapts;
421+
progress=false,
422+
)
423+
θ = reduce(vcat, (permutedims(s) for s in samples))
424+
# 1st marginal
425+
@test w1(θ[:, 1], x_true) < tol_w1
426+
# 2nd marginal
427+
@test w1(θ[:, 2], y_true) < tol_w1
428+
end
429+
430+
@testset "SoftAbsRiemannianMetric" begin
431+
# We do not need SoftAbs for Gaussian target, so using small α
432+
metric = SoftAbsRiemannianMetric((D,), G, ∂G∂θ, 1.0)
433+
kinetic = GaussianKinetic()
434+
hamiltonian = Hamiltonian(metric, kinetic, ℓπ, ∂ℓπ∂θ)
435+
436+
initial_ϵ = 0.01
437+
integrator = GeneralizedLeapfrog(initial_ϵ, 15)
438+
kernel = HMCKernel(Trajectory{MultinomialTS}(integrator, GeneralisedNoUTurn()))
439+
440+
acceptance_rate = 0.9
441+
adaptor = StepSizeAdaptor(acceptance_rate, integrator)
442+
443+
samples, stats = sample(
444+
rng,
445+
hamiltonian,
446+
kernel,
447+
initial_θ,
448+
n_samples,
449+
adaptor,
450+
n_adapts;
451+
progress=false,
452+
)
453+
454+
θ = reduce(vcat, (permutedims(s) for s in samples))
455+
# 1st marginal
456+
@test w1(θ[:, 1], x_true) < tol_w1
457+
# 2nd marginal
458+
@test w1(θ[:, 2], y_true) < tol_w1
459+
end
394460
end
395461

396-
@testset "SoftAbsRiemannianMetric" begin
397-
# We do not need SoftAbs for Gaussian target, so using small α
398-
metric = SoftAbsRiemannianMetric((D,), G, ∂G∂θ, 1.0)
399-
kinetic = GaussianKinetic()
400-
hamiltonian = Hamiltonian(metric, kinetic, ℓπ, ∂ℓπ∂θ)
462+
@testset "Validation testing (Funnel)" begin
463+
464+
# Funnel i.i.d. sampler
465+
# θ layout: [v, x1]
466+
function funnel_iid(rng::AbstractRNG, n::Int)
467+
v = 3.0 .* randn(rng, n)
468+
X = Matrix{Float64}(undef, n, 1)
469+
for i in 1:n
470+
s = exp(v[i] / 2)
471+
@inbounds X[i, :] .= s .* randn(rng, 1)
472+
end
473+
return v, X
474+
end
401475

402-
initial_ϵ = 0.01
403-
integrator = GeneralizedLeapfrog(initial_ϵ, 15)
404-
kernel = HMCKernel(Trajectory{MultinomialTS}(integrator, GeneralisedNoUTurn()))
405-
406-
acceptance_rate = 0.9
407-
adaptor = StepSizeAdaptor(acceptance_rate, integrator)
408-
409-
samples, stats = sample(
410-
rng,
411-
hamiltonian,
412-
kernel,
413-
initial_θ,
414-
n_samples,
415-
adaptor,
416-
n_adapts;
417-
progress=false,
476+
# 1D Wasserstein-1 distance tolerances for Funnel marginals
477+
function funnel_w1_tols(;
478+
n::Int,
479+
reps::Int=200,
480+
q::Float64=0.999,
481+
inflate::Float64=2.0,
482+
rng::AbstractRNG=Random.default_rng(),
418483
)
419-
@test mean(samples) zeros(D) atol = mean_tol
420-
@test Statistics.var(samples) ones(D) atol = var_tol
421-
end
422-
end
484+
vals_v = Vector{Float64}(undef, reps)
485+
vals_x1 = Vector{Float64}(undef, reps)
423486

424-
@testset "Validation testing (Funnel)" begin
487+
for i in 1:reps
488+
vA, XA = funnel_iid(rng, n)
489+
vB, XB = funnel_iid(rng, n)
425490

426-
# 1D Wasserstein-1 distance
427-
function w1(a::AbstractVector, b::AbstractVector)
428-
sa = sort(a)
429-
sb = sort(b)
430-
return mean(abs.(sa .- sb))
431-
end
491+
x1A = XA[:, 1]
492+
x1B = XB[:, 1]
432493

433-
target = Funnel()
434-
rng = MersenneTwister(234)
435-
λ = 1e-2
494+
vals_v[i] = w1(vA, vB)
495+
vals_x1[i] = w1(x1A, x1B)
496+
end
436497

437-
initial_θ = rand(rng, dim(target))
498+
sort!(vals_v)
499+
sort!(vals_x1)
500+
idx = clamp(ceil(Int, q * reps), 1, reps)
438501

439-
ℓπ = MCMCLogDensityProblems.gen_logpdf(target)
440-
∂ℓπ∂θ = MCMCLogDensityProblems.gen_logpdf_grad(target, initial_θ)
502+
return (tol_v=inflate * vals_v[idx], tol_x1=inflate * vals_x1[idx])
503+
end
441504

442-
_, _, G, ∂G∂θ = prepare_sample(ℓπ, initial_θ, λ)
505+
target = Funnel()
506+
rng = MersenneTwister(234)
507+
λ = 1e-2
443508

444-
D = dim(target)
509+
initial_θ = rand(rng, dim(target))
445510

446-
n_samples = 1000
447-
n_adapts = 500
511+
ℓπ = MCMCLogDensityProblems.gen_logpdf(target)
512+
∂ℓπ∂θ = MCMCLogDensityProblems.gen_logpdf_grad(target, initial_θ)
448513

449-
# True samples
450-
v_true = 3 .* randn(rng, n_samples)
451-
X_true = Matrix{Float64}(undef, n_samples, 1)
452-
for n in 1:n_samples
453-
s = exp(v_true[n] / 2)
454-
@inbounds X_true[n, :] .= s .* randn(rng, 1)
455-
end
514+
_, _, G, ∂G∂θ = prepare_sample(ℓπ, initial_θ, λ)
456515

457-
tol_1 = 10 / sqrt(n_samples)
458-
tol_2 = 30 / sqrt(n_samples)
516+
D = dim(target)
459517

460-
@testset "SoftAbsRiemannianMetric" begin
461-
metric = SoftAbsRiemannianMetric((D,), G, ∂G∂θ, 20.0)
462-
kinetic = GaussianKinetic()
463-
hamiltonian = Hamiltonian(metric, kinetic, ℓπ, ∂ℓπ∂θ)
518+
n_samples = 1000
519+
n_adapts = 500
464520

465-
initial_ϵ = 0.01
466-
integrator = GeneralizedLeapfrog(initial_ϵ, 15)
467-
kernel = HMCKernel(Trajectory{MultinomialTS}(integrator, GeneralisedNoUTurn()))
468-
469-
acceptance_rate = 0.9
470-
adaptor = StepSizeAdaptor(acceptance_rate, integrator)
471-
472-
samples, stats = sample(
473-
rng,
474-
hamiltonian,
475-
kernel,
476-
initial_θ,
477-
n_samples,
478-
adaptor,
479-
n_adapts;
480-
progress=false,
481-
)
521+
# True samples
522+
v_true, X_true = funnel_iid(rng, n_samples)
523+
524+
# Wasserstein-1 distance tolerances
525+
tols = funnel_w1_tols(; n=n_samples, rng=rng)
526+
527+
@testset "SoftAbsRiemannianMetric" begin
528+
metric = SoftAbsRiemannianMetric((D,), G, ∂G∂θ, 20.0)
529+
kinetic = GaussianKinetic()
530+
hamiltonian = Hamiltonian(metric, kinetic, ℓπ, ∂ℓπ∂θ)
482531

483-
θ = reduce(vcat, (permutedims(s) for s in samples))
484-
# 1st marginal
485-
@test w1(θ[:, 1], v_true) < tol_1
486-
# 2nd marginal
487-
@test w1(θ[:, 2], X_true[:, 1]) < tol_2
532+
initial_ϵ = 0.01
533+
integrator = GeneralizedLeapfrog(initial_ϵ, 15)
534+
kernel = HMCKernel(Trajectory{MultinomialTS}(integrator, GeneralisedNoUTurn()))
535+
536+
acceptance_rate = 0.9
537+
adaptor = StepSizeAdaptor(acceptance_rate, integrator)
538+
539+
samples, stats = sample(
540+
rng,
541+
hamiltonian,
542+
kernel,
543+
initial_θ,
544+
n_samples,
545+
adaptor,
546+
n_adapts;
547+
progress=false,
548+
)
549+
550+
θ = reduce(vcat, (permutedims(s) for s in samples))
551+
# 1st marginal
552+
@test w1(θ[:, 1], v_true) < tols.tol_v
553+
# 2nd marginal
554+
@test w1(θ[:, 2], X_true[:, 1]) < tols.tol_x1
555+
end
488556
end
489557
end

0 commit comments

Comments
 (0)