diff --git a/.github/workflows/Benchmarking.yml b/.github/workflows/Benchmarking.yml deleted file mode 100644 index 7c565275f..000000000 --- a/.github/workflows/Benchmarking.yml +++ /dev/null @@ -1,125 +0,0 @@ -name: Benchmarking - -on: - pull_request: - -jobs: - benchmark-base: - runs-on: ubuntu-latest - outputs: - results: ${{ steps.benchmark.outputs.results }} - sha: ${{ steps.benchmark.outputs.sha }} - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ github.base_ref }} - - uses: julia-actions/setup-julia@v2 - with: - version: '1.11' - - uses: julia-actions/cache@v2 - - - name: Run benchmarks - id: benchmark - working-directory: ./benchmarks - run: | - # github output can't handle more than 1 line, hence the tail - julia --project=. -e 'using Pkg; Pkg.instantiate()' - results=$(julia --project=. benchmarks.jl json | tail -n 1 || true) - echo $results - echo "results=$results" >> "$GITHUB_OUTPUT" - echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" - - benchmark-head: - runs-on: ubuntu-latest - outputs: - results: ${{ steps.benchmark.outputs.results }} - sha: ${{ steps.benchmark.outputs.sha }} - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ github.event.pull_request.head.sha }} - - uses: julia-actions/setup-julia@v2 - with: - version: '1.11' - - uses: julia-actions/cache@v2 - - - name: Run benchmarks - id: benchmark - working-directory: ./benchmarks - run: | - # github output can't handle more than 1 line, hence the tail - julia --project=. -e 'using Pkg; Pkg.instantiate()' - results=$(julia --project=. benchmarks.jl json | tail -n 1 || true) - echo $results - echo "results=$results" >> "$GITHUB_OUTPUT" - echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" - - combine-results: - runs-on: ubuntu-latest - needs: [benchmark-base, benchmark-head] - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ github.event.pull_request.head.sha }} - - uses: julia-actions/setup-julia@v2 - with: - version: '1.11' - - uses: julia-actions/cache@v2 - - - name: Combine benchmark results - working-directory: ./benchmarks - run: | - version_info=$(julia -e 'using InteractiveUtils; versioninfo()') - echo "$version_info" - echo "VERSION_INFO<> $GITHUB_ENV - echo "$version_info" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - # save outputs of previous jobs to json file - echo "Base results" - echo "--------------------------------------------------------" - echo '${{needs.benchmark-base.outputs.results}}' - echo '${{needs.benchmark-base.outputs.results}}' > base.json - echo "Head results" - echo "--------------------------------------------------------" - echo '${{needs.benchmark-head.outputs.results}}' - echo '${{needs.benchmark-head.outputs.results}}' > head.json - - # combine them and save the output as an env var for later steps - julia --project=. -e 'using Pkg; Pkg.instantiate()' - results=$(julia --project=. benchmarks.jl combine head.json base.json) - echo "Combined results" - echo "--------------------------------------------------------" - echo "$results" - - echo "BENCHMARK_OUTPUT<> $GITHUB_ENV - echo "$results" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - - name: Find existing benchmark comment - uses: peter-evans/find-comment@v4 - id: find_comment - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: github-actions[bot] - - - name: Create or update benchmark comment - uses: peter-evans/create-or-update-comment@v5 - with: - issue-number: ${{ github.event.pull_request.number }} - body: | - ## Benchmark Report - - - this PR's head: `${{ needs.benchmark-head.outputs.sha }}` - - base branch: `${{ needs.benchmark-base.outputs.sha }}` - - ### Computer Information - ``` - ${{ env.VERSION_INFO }} - ``` - ### Benchmark Results - - ${{ env.BENCHMARK_OUTPUT }} - - comment-id: ${{ steps.find_comment.outputs.comment-id }} - edit-mode: replace diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5f918e89e..7e313cc6c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -31,29 +31,29 @@ jobs: - version: '1' os: ubuntu-latest num_threads: 2 - # Minimum supported version - - version: 'min' - os: ubuntu-latest - num_threads: 2 - # 1.11 - - version: '1.11' - os: ubuntu-latest - num_threads: 2 - # Single-threaded - - version: '1' - os: ubuntu-latest - num_threads: 1 - # Windows + # # Minimum supported version + # - version: 'min' + # os: ubuntu-latest + # num_threads: 2 + # # 1.11 + # - version: '1.11' + # os: ubuntu-latest + # num_threads: 2 + # # Single-threaded + # - version: '1' + # os: ubuntu-latest + # num_threads: 1 + # # Windows - version: '1' os: windows-latest num_threads: 2 - # macOS - - version: '1' - os: macos-latest - num_threads: 2 + # # macOS + # - version: '1' + # os: macos-latest + # num_threads: 2 test_group: - Group1 - - Group2 + # - Group2 steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/DocTest.yml b/.github/workflows/DocTest.yml deleted file mode 100644 index 1a7589299..000000000 --- a/.github/workflows/DocTest.yml +++ /dev/null @@ -1,41 +0,0 @@ -# We want to only run doctests on a single version of Julia, because -# things like error messages / output can change between versions and -# is fragile to test against. -name: Doctests - -on: - push: - branches: - - main - pull_request: - merge_group: - types: [checks_requested] - -# needed to allow julia-actions/cache to delete old caches that it has created -permissions: - actions: write - contents: read - -# Cancel existing tests on the same PR if a new commit is added to a pull request -concurrency: - group: ${{ github.workflow }}-${{ github.ref || github.run_id }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v6 - - - uses: julia-actions/setup-julia@v2 - with: - version: '1' - - - uses: julia-actions/cache@v2 - - - uses: julia-actions/julia-buildpkg@v1 - - - uses: julia-actions/julia-runtest@v1 - env: - GROUP: Doctests diff --git a/.github/workflows/Docs.yml b/.github/workflows/Docs.yml deleted file mode 100644 index 878fb63d1..000000000 --- a/.github/workflows/Docs.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Documentation - -on: - push: - branches: - - main - tags: '*' - pull_request: - merge_group: - types: [checks_requested] - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -permissions: - contents: write - pull-requests: write - -jobs: - docs: - runs-on: ubuntu-latest - - steps: - - name: Build and deploy Documenter.jl docs - uses: TuringLang/actions/DocsDocumenter@main diff --git a/.github/workflows/Enzyme.yml b/.github/workflows/Enzyme.yml deleted file mode 100644 index 167e53b94..000000000 --- a/.github/workflows/Enzyme.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Enzyme on demo models - -on: - push: - branches: - - main - pull_request: - -# needed to allow julia-actions/cache to delete old caches that it has created -permissions: - actions: write - contents: read - -# Cancel existing tests on the same PR if a new commit is added to a pull request -concurrency: - group: ${{ github.workflow }}-${{ github.ref || github.run_id }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - enzyme: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - uses: julia-actions/setup-julia@v2 - with: - version: "1.11" - - - uses: julia-actions/cache@v2 - - - name: Run AD with Enzyme on demo models - working-directory: test/integration/enzyme - run: | - julia --project=. --color=yes -e 'using Pkg; Pkg.instantiate()' - julia --project=. --color=yes main.jl diff --git a/.github/workflows/Format.yml b/.github/workflows/Format.yml deleted file mode 100644 index 259e056c7..000000000 --- a/.github/workflows/Format.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Format - -on: - push: - branches: - - main - pull_request: - merge_group: - types: [checks_requested] - -concurrency: - # Skip intermediate builds: always. - # Cancel intermediate builds: only if it is a pull request build. - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} - -jobs: - format: - runs-on: ubuntu-latest - - steps: - - name: Format code - uses: TuringLang/actions/Format@main diff --git a/docs/src/api.md b/docs/src/api.md index 193a6ce4c..686549e9b 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -267,6 +267,7 @@ DynamicPPL provides several demo models in the `DynamicPPL.TestUtils` submodule. ```@docs DynamicPPL.TestUtils.DEMO_MODELS +DynamicPPL.TestUtils.ALL_MODELS ``` For every demo model, one can define the true log prior, log likelihood, and log joint probabilities. diff --git a/src/abstract_varinfo.jl b/src/abstract_varinfo.jl index ec5e1ea10..86dd0f9dc 100644 --- a/src/abstract_varinfo.jl +++ b/src/abstract_varinfo.jl @@ -838,10 +838,14 @@ function link!!( t::StaticTransformation{<:Bijectors.Transform}, vi::AbstractVarInfo, ::Model ) b = inverse(t.bijector) + @show b x = vi[:] + @show x y, logjac = with_logabsdet_jacobian(b, x) + @show y, logjac # Set parameters and add the logjac term. vi = unflatten(vi, y) + @show vi[:] if hasacc(vi, Val(:LogJacobian)) vi = acclogjac!!(vi, logjac) end @@ -866,7 +870,7 @@ end function link(vi::AbstractVarInfo, vns::VarNameTuple, model::Model) return link(default_transformation(model, vi), vi, vns, model) end -function link(t::DynamicTransformation, vi::AbstractVarInfo, model::Model) +function link(t::AbstractTransformation, vi::AbstractVarInfo, model::Model) return link!!(t, deepcopy(vi), model) end @@ -932,7 +936,7 @@ end function invlink(vi::AbstractVarInfo, vns::VarNameTuple, model::Model) return invlink(default_transformation(model, vi), vi, vns, model) end -function invlink(t::DynamicTransformation, vi::AbstractVarInfo, model::Model) +function invlink(t::AbstractTransformation, vi::AbstractVarInfo, model::Model) return invlink!!(t, deepcopy(vi), model) end diff --git a/src/test_utils/model_interface.jl b/src/test_utils/model_interface.jl index cb949464e..19422bc86 100644 --- a/src/test_utils/model_interface.jl +++ b/src/test_utils/model_interface.jl @@ -92,7 +92,9 @@ Even though it is recommended to implement this by hand for a particular `Model` a default implementation using [`SimpleVarInfo{<:Dict}`](@ref) is provided. """ function varnames(model::Model) - return collect(keys(last(DynamicPPL.init!!(model, SimpleVarInfo(Dict()))))) + result = collect(keys(last(DynamicPPL.init!!(model, SimpleVarInfo(Dict()))))) + # Concretise the element type. + return [x for x in result] end """ diff --git a/src/test_utils/models.jl b/src/test_utils/models.jl index 8ffb7cbdf..3a73ad4f8 100644 --- a/src/test_utils/models.jl +++ b/src/test_utils/models.jl @@ -34,6 +34,11 @@ function logprior_true_with_logabsdet_jacobian( x_unconstrained, Δlogp = Bijectors.with_logabsdet_jacobian(b_x, x) return (m=m, x=x_unconstrained), logprior_true(model, m, x) - Δlogp end +function rand_prior_true(rng::Random.AbstractRNG, ::Model{typeof(demo_dynamic_constraint)}) + m = rand(rng, Normal()) + x = rand(rng, truncated(Normal(); lower=m)) + return (m=m, x=x) +end """ demo_one_variable_multiple_constraints() @@ -109,12 +114,12 @@ x ~ LKJCholesky(d, 1.0) ``` """ @model function demo_lkjchol(d::Int=2) - x ~ LKJCholesky(d, 1.0) + x ~ LKJCholesky(d, 1.5) return (x=x,) end function logprior_true(model::Model{typeof(demo_lkjchol)}, x) - return logpdf(LKJCholesky(model.args.d, 1.0), x) + return logpdf(LKJCholesky(model.args.d, 1.5), x) end function loglikelihood_true(model::Model{typeof(demo_lkjchol)}, x) @@ -163,6 +168,9 @@ end function loglikelihood_true(::Model{typeof(demo_static_transformation)}, s, m) return logpdf(Normal(m, sqrt(s)), 1.5) + logpdf(Normal(m, sqrt(s)), 2.0) end +function varnames(::Model{typeof(demo_static_transformation)}) + return [@varname(s), @varname(m)] +end function logprior_true_with_logabsdet_jacobian( model::Model{typeof(demo_static_transformation)}, s, m ) @@ -557,22 +565,6 @@ function varnames(model::Model{typeof(demo_assume_matrix_observe_matrix_index)}) return [@varname(s), @varname(m)] end -const DemoModels = Union{ - Model{typeof(demo_dot_assume_observe)}, - Model{typeof(demo_assume_index_observe)}, - Model{typeof(demo_assume_multivariate_observe)}, - Model{typeof(demo_dot_assume_observe_index)}, - Model{typeof(demo_assume_dot_observe)}, - Model{typeof(demo_assume_dot_observe_literal)}, - Model{typeof(demo_assume_observe_literal)}, - Model{typeof(demo_assume_multivariate_observe_literal)}, - Model{typeof(demo_dot_assume_observe_index_literal)}, - Model{typeof(demo_assume_submodel_observe_index_literal)}, - Model{typeof(demo_dot_assume_observe_submodel)}, - Model{typeof(demo_dot_assume_observe_matrix_index)}, - Model{typeof(demo_assume_matrix_observe_matrix_index)}, -} - const UnivariateAssumeDemoModels = Union{ Model{typeof(demo_assume_dot_observe)}, Model{typeof(demo_assume_dot_observe_literal)}, @@ -758,3 +750,14 @@ const DEMO_MODELS = ( demo_dot_assume_observe_matrix_index(), demo_assume_matrix_observe_matrix_index(), ) + +""" +A tuple of all models defined in DynamicPPL.TestUtils. +""" +const ALL_MODELS = ( + # DEMO_MODELS..., + # demo_dynamic_constraint(), + # demo_one_variable_multiple_constraints(), + # demo_lkjchol(), + demo_static_transformation(), +) diff --git a/src/test_utils/varinfo.jl b/src/test_utils/varinfo.jl index 26e2aa7ca..6483b29e8 100644 --- a/src/test_utils/varinfo.jl +++ b/src/test_utils/varinfo.jl @@ -10,7 +10,14 @@ Test that `vi[vn]` corresponds to the correct value in `vals` for every `vn` in """ function test_values(vi::AbstractVarInfo, vals::NamedTuple, vns; compare=isequal, kwargs...) for vn in vns - @test compare(vi[vn], get(vals, vn); kwargs...) + val = get(vals, vn) + # TODO(mhauru) Workaround for https://github.com/JuliaLang/LinearAlgebra.jl/pull/1404 + # Remove once the fix is all Julia versions we support. + if val isa Cholesky + @test compare(vi[vn].L, val.L; kwargs...) + else + @test compare(vi[vn], val; kwargs...) + end end end diff --git a/src/threadsafe.jl b/src/threadsafe.jl index 6d3acce6c..c7ab106a2 100644 --- a/src/threadsafe.jl +++ b/src/threadsafe.jl @@ -85,12 +85,24 @@ function invlink!!(t::AbstractTransformation, vi::ThreadSafeVarInfo, args...) return Accessors.@set vi.varinfo = invlink!!(t, vi.varinfo, args...) end -function link(t::AbstractTransformation, vi::ThreadSafeVarInfo, args...) - return Accessors.@set vi.varinfo = link(t, vi.varinfo, args...) +function link(t::AbstractTransformation, vi::ThreadSafeVarInfo, model::Model) + return Accessors.@set vi.varinfo = link(t, vi.varinfo, model) end -function invlink(t::AbstractTransformation, vi::ThreadSafeVarInfo, args...) - return Accessors.@set vi.varinfo = invlink(t, vi.varinfo, args...) +function invlink(t::AbstractTransformation, vi::ThreadSafeVarInfo, model::Model) + return Accessors.@set vi.varinfo = invlink(t, vi.varinfo, model) +end + +function link( + t::AbstractTransformation, vi::ThreadSafeVarInfo, vns::VarNameTuple, model::Model +) + return Accessors.@set vi.varinfo = link(t, vi.varinfo, vns, model) +end + +function invlink( + t::AbstractTransformation, vi::ThreadSafeVarInfo, vns::VarNameTuple, model::Model +) + return Accessors.@set vi.varinfo = invlink(t, vi.varinfo, vns, model) end # Need to define explicitly for `DynamicTransformation` to avoid method ambiguity. diff --git a/test/chains.jl b/test/chains.jl index 498e2e912..36c274b62 100644 --- a/test/chains.jl +++ b/test/chains.jl @@ -67,7 +67,7 @@ using Test end @testset "ParamsWithStats from LogDensityFunction" begin - @testset "$(m.f)" for m in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(m.f)" for m in DynamicPPL.TestUtils.ALL_MODELS unlinked_vi = VarInfo(m) @testset "$islinked" for islinked in (false, true) vi = if islinked diff --git a/test/contexts.jl b/test/contexts.jl index ae7332a43..5ff632837 100644 --- a/test/contexts.jl +++ b/test/contexts.jl @@ -179,7 +179,7 @@ Base.IteratorEltype(::Type{<:AbstractContext}) = Base.EltypeUnknown() @test new_ctx == FixedContext((b=4,), ConditionContext((a=1,))) end - @testset "evaluation: $(model.f)" for model in DynamicPPL.TestUtils.DEMO_MODELS + @testset "evaluation: $(model.f)" for model in DynamicPPL.TestUtils.ALL_MODELS prefix_vn = @varname(my_prefix) context = DynamicPPL.PrefixContext(prefix_vn, DefaultContext()) new_model = contextualize(model, context) diff --git a/test/ext/DynamicPPLJETExt.jl b/test/ext/DynamicPPLJETExt.jl index c74beefdb..e46c25113 100644 --- a/test/ext/DynamicPPLJETExt.jl +++ b/test/ext/DynamicPPLJETExt.jl @@ -61,7 +61,21 @@ end @testset "demo models" begin - @testset "$(model.f)" for model in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(model.f)" for model in DynamicPPL.TestUtils.ALL_MODELS + if model.f === DynamicPPL.TestUtils.demo_lkjchol + # TODO(mhauru) + # The LKJCholesky model fails with JET. The problem is not with Turing but + # with Distributions, and ultimately this in LinearAlgebra: + # julia> v = @view rand(2,2)[:,1]; + # + # julia> JET.@report_call norm(v) + # ═════ 2 possible errors found ═════ + # blahblah + # The below trivial call to @test is just marking that there's something + # broken here. + @test false broken = true + continue + end # Use debug logging below. varinfo = DynamicPPL.Experimental.determine_suitable_varinfo(model) # Check that the inferred varinfo is indeed suitable for evaluation diff --git a/test/integration/enzyme/main.jl b/test/integration/enzyme/main.jl index edfd67d18..b99607aeb 100644 --- a/test/integration/enzyme/main.jl +++ b/test/integration/enzyme/main.jl @@ -1,4 +1,4 @@ -using DynamicPPL.TestUtils: DEMO_MODELS +using DynamicPPL.TestUtils: ALL_MODELS using DynamicPPL.TestUtils.AD: run_ad using ADTypes: AutoEnzyme using Test: @test, @testset @@ -17,7 +17,7 @@ ADTYPES = ( ) @testset "$ad_key" for (ad_key, ad_type) in ADTYPES - @testset "$(model.f)" for model in DEMO_MODELS + @testset "$(model.f)" for model in ALL_MODELS @test run_ad(model, ad_type) isa Any end end diff --git a/test/logdensityfunction.jl b/test/logdensityfunction.jl index 383d7593d..011cb22ce 100644 --- a/test/logdensityfunction.jl +++ b/test/logdensityfunction.jl @@ -16,7 +16,7 @@ using ReverseDiff: ReverseDiff using Mooncake: Mooncake @testset "LogDensityFunction: Correctness" begin - @testset "$(m.f)" for m in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(m.f)" for m in DynamicPPL.TestUtils.ALL_MODELS @testset "$varinfo_func" for varinfo_func in [ DynamicPPL.untyped_varinfo, DynamicPPL.typed_varinfo, @@ -107,7 +107,7 @@ end end @testset "LogDensityFunction: Type stability" begin - @testset "$(m.f)" for m in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(m.f)" for m in DynamicPPL.TestUtils.ALL_MODELS unlinked_vi = DynamicPPL.VarInfo(m) @testset "$islinked" for islinked in (false, true) vi = if islinked @@ -163,7 +163,7 @@ end ] @testset "Correctness" begin - @testset "$(m.f)" for m in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(m.f)" for m in DynamicPPL.TestUtils.ALL_MODELS varinfo = VarInfo(m) linked_varinfo = DynamicPPL.link(varinfo, m) f = LogDensityFunction(m, getlogjoint_internal, linked_varinfo) @@ -187,7 +187,7 @@ end end @testset "logdensity_and_gradient with views" begin - # This test ensures that you can call `logdensity_and_gradient` with an array + # This test ensures that you can call `logdensity_and_gradient` with an array # type that isn't the same as the one used in the gradient preparation. @model function f() x ~ Normal() diff --git a/test/model.jl b/test/model.jl index 6da5ea246..c878fd905 100644 --- a/test/model.jl +++ b/test/model.jl @@ -431,7 +431,7 @@ const GDEMO_DEFAULT = DynamicPPL.TestUtils.demo_assume_observe_literal() end @testset "values_as_in_model" begin - @testset "$(model.f)" for model in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(model.f)" for model in DynamicPPL.TestUtils.ALL_MODELS vns = DynamicPPL.TestUtils.varnames(model) example_values = DynamicPPL.TestUtils.rand_prior_true(model) varinfos = DynamicPPL.TestUtils.setup_varinfos(model, example_values, vns) diff --git a/test/model_utils.jl b/test/model_utils.jl index af695dbf2..1547ad276 100644 --- a/test/model_utils.jl +++ b/test/model_utils.jl @@ -1,6 +1,6 @@ @testset "model_utils.jl" begin @testset "value_iterator_from_chain" begin - @testset "$model" for model in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$model" for model in DynamicPPL.TestUtils.ALL_MODELS # Check that the values generated by value_iterator_from_chain # match the values in the original chain chain = make_chain_from_prior(model, 10) diff --git a/test/runtests.jl b/test/runtests.jl index 9649aebbb..6460aad89 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -43,61 +43,62 @@ Random.seed!(100) include("test_util.jl") @testset verbose = true "DynamicPPL.jl" begin - # The tests are split into two groups so that CI can run in parallel. The - # groups are chosen to make both groups take roughly the same amount of - # time, but beyond that there is no particular reason for the split. - if GROUP == "All" || GROUP == "Group1" - if AQUA - include("Aqua.jl") - end - include("utils.jl") - include("accumulators.jl") - include("compiler.jl") - include("varnamedvector.jl") - include("varinfo.jl") - include("simple_varinfo.jl") - include("model.jl") - include("distribution_wrappers.jl") - include("linking.jl") - include("serialization.jl") - include("pointwise_logdensities.jl") - include("lkj.jl") - include("contexts.jl") - include("context_implementations.jl") - include("threadsafe.jl") - include("debug_utils.jl") - include("submodels.jl") - include("chains.jl") - end + # # The tests are split into two groups so that CI can run in parallel. The + # # groups are chosen to make both groups take roughly the same amount of + # # time, but beyond that there is no particular reason for the split. + # if GROUP == "All" || GROUP == "Group1" + # if AQUA + # include("Aqua.jl") + # end + # include("utils.jl") + # include("accumulators.jl") + # include("compiler.jl") + # include("varnamedvector.jl") + # include("varnamedtuple.jl") + # include("varinfo.jl") + include("simple_varinfo.jl") + # include("model.jl") + # include("distribution_wrappers.jl") + # include("linking.jl") + # include("serialization.jl") + # include("pointwise_logdensities.jl") + # include("lkj.jl") + # include("contexts.jl") + # include("context_implementations.jl") + # include("threadsafe.jl") + # include("debug_utils.jl") + # include("submodels.jl") + # include("chains.jl") + # end - if GROUP == "All" || GROUP == "Group2" - include("bijector.jl") - include("logdensityfunction.jl") - @testset "extensions" begin - include("ext/DynamicPPLMCMCChainsExt.jl") - include("ext/DynamicPPLJETExt.jl") - include("ext/DynamicPPLMarginalLogDensitiesExt.jl") - end - @testset "ad" begin - include("ext/DynamicPPLForwardDiffExt.jl") - include("ext/DynamicPPLMooncakeExt.jl") - end - @testset "prob and logprob macro" begin - @test_throws ErrorException prob"..." - @test_throws ErrorException logprob"..." - end - end + # if GROUP == "All" || GROUP == "Group2" + # include("bijector.jl") + # include("logdensityfunction.jl") + # @testset "extensions" begin + # include("ext/DynamicPPLMCMCChainsExt.jl") + # include("ext/DynamicPPLJETExt.jl") + # include("ext/DynamicPPLMarginalLogDensitiesExt.jl") + # end + # @testset "ad" begin + # include("ext/DynamicPPLForwardDiffExt.jl") + # include("ext/DynamicPPLMooncakeExt.jl") + # end + # @testset "prob and logprob macro" begin + # @test_throws ErrorException prob"..." + # @test_throws ErrorException logprob"..." + # end + # end - if GROUP == "All" || GROUP == "Doctests" - DocMeta.setdocmeta!( - DynamicPPL, :DocTestSetup, :(using DynamicPPL, Distributions); recursive=true - ) - doctestfilters = [ - # Ignore the source of a warning in the doctest output, since this is dependent on host. - # This is a line that starts with "└ @ " and ends with the line number. - r"└ @ .+:[0-9]+", - ] + # if GROUP == "All" || GROUP == "Doctests" + # DocMeta.setdocmeta!( + # DynamicPPL, :DocTestSetup, :(using DynamicPPL, Distributions); recursive=true + # ) + # doctestfilters = [ + # # Ignore the source of a warning in the doctest output, since this is dependent on host. + # # This is a line that starts with "└ @ " and ends with the line number. + # r"└ @ .+:[0-9]+", + # ] - doctest(DynamicPPL; manual=false, doctestfilters=doctestfilters) - end + # doctest(DynamicPPL; manual=false, doctestfilters=doctestfilters) + # end end diff --git a/test/simple_varinfo.jl b/test/simple_varinfo.jl index 488cb8941..4628d2898 100644 --- a/test/simple_varinfo.jl +++ b/test/simple_varinfo.jl @@ -1,100 +1,109 @@ @testset "simple_varinfo.jl" begin - @testset "constructor & indexing" begin - @testset "NamedTuple" begin - svi = SimpleVarInfo(; m=1.0) - @test getlogjoint(svi) == 0.0 - @test haskey(svi, @varname(m)) - @test !haskey(svi, @varname(m[1])) - - svi = SimpleVarInfo(; m=[1.0]) - @test getlogjoint(svi) == 0.0 - @test haskey(svi, @varname(m)) - @test haskey(svi, @varname(m[1])) - @test !haskey(svi, @varname(m[2])) - @test svi[@varname(m)][1] == svi[@varname(m[1])] - - svi = SimpleVarInfo(; m=(a=[1.0],)) - @test haskey(svi, @varname(m)) - @test haskey(svi, @varname(m.a)) - @test haskey(svi, @varname(m.a[1])) - @test !haskey(svi, @varname(m.a[2])) - @test !haskey(svi, @varname(m.a.b)) - - svi = SimpleVarInfo{Float32}(; m=1.0) - @test getlogjoint(svi) isa Float32 - - svi = SimpleVarInfo((m=1.0,)) - svi = accloglikelihood!!(svi, 1.0) - @test getlogjoint(svi) == 1.0 - end - - @testset "Dict" begin - svi = SimpleVarInfo(Dict(@varname(m) => 1.0)) - @test getlogjoint(svi) == 0.0 - @test haskey(svi, @varname(m)) - @test !haskey(svi, @varname(m[1])) - - svi = SimpleVarInfo(Dict(@varname(m) => [1.0])) - @test getlogjoint(svi) == 0.0 - @test haskey(svi, @varname(m)) - @test haskey(svi, @varname(m[1])) - @test !haskey(svi, @varname(m[2])) - @test svi[@varname(m)][1] == svi[@varname(m[1])] - - svi = SimpleVarInfo(Dict(@varname(m) => (a=[1.0],))) - @test haskey(svi, @varname(m)) - @test haskey(svi, @varname(m.a)) - @test haskey(svi, @varname(m.a[1])) - @test !haskey(svi, @varname(m.a[2])) - @test !haskey(svi, @varname(m.a.b)) - - svi = SimpleVarInfo(Dict(@varname(m.a) => [1.0])) - # Now we only have a variable `m.a` which is subsumed by `m`, - # but we can't guarantee that we have the "entire" `m`. - @test !haskey(svi, @varname(m)) - @test haskey(svi, @varname(m.a)) - @test haskey(svi, @varname(m.a[1])) - @test !haskey(svi, @varname(m.a[2])) - @test !haskey(svi, @varname(m.a.b)) - end - - @testset "VarNamedVector" begin - svi = SimpleVarInfo(push!!(DynamicPPL.VarNamedVector(), @varname(m) => 1.0)) - @test getlogjoint(svi) == 0.0 - @test haskey(svi, @varname(m)) - @test !haskey(svi, @varname(m[1])) - - svi = SimpleVarInfo(push!!(DynamicPPL.VarNamedVector(), @varname(m) => [1.0])) - @test getlogjoint(svi) == 0.0 - @test haskey(svi, @varname(m)) - @test haskey(svi, @varname(m[1])) - @test !haskey(svi, @varname(m[2])) - @test svi[@varname(m)][1] == svi[@varname(m[1])] - - svi = SimpleVarInfo(push!!(DynamicPPL.VarNamedVector(), @varname(m.a) => [1.0])) - @test haskey(svi, @varname(m)) - @test haskey(svi, @varname(m.a)) - @test haskey(svi, @varname(m.a[1])) - @test !haskey(svi, @varname(m.a[2])) - @test !haskey(svi, @varname(m.a.b)) - # The implementation of haskey and getvalue fo VarNamedVector is incomplete, the - # next test is here to remind of us that. - svi = SimpleVarInfo( - push!!(DynamicPPL.VarNamedVector(), @varname(m.a.b) => [1.0]) - ) - @test_broken !haskey(svi, @varname(m.a.b.c.d)) - end - end + # @testset "constructor & indexing" begin + # @testset "NamedTuple" begin + # svi = SimpleVarInfo(; m=1.0) + # @test getlogjoint(svi) == 0.0 + # @test haskey(svi, @varname(m)) + # @test !haskey(svi, @varname(m[1])) + # + # svi = SimpleVarInfo(; m=[1.0]) + # @test getlogjoint(svi) == 0.0 + # @test haskey(svi, @varname(m)) + # @test haskey(svi, @varname(m[1])) + # @test !haskey(svi, @varname(m[2])) + # @test svi[@varname(m)][1] == svi[@varname(m[1])] + # + # svi = SimpleVarInfo(; m=(a=[1.0],)) + # @test haskey(svi, @varname(m)) + # @test haskey(svi, @varname(m.a)) + # @test haskey(svi, @varname(m.a[1])) + # @test !haskey(svi, @varname(m.a[2])) + # @test !haskey(svi, @varname(m.a.b)) + # + # svi = SimpleVarInfo{Float32}(; m=1.0) + # @test getlogjoint(svi) isa Float32 + # + # svi = SimpleVarInfo((m=1.0,)) + # svi = accloglikelihood!!(svi, 1.0) + # @test getlogjoint(svi) == 1.0 + # end + # + # @testset "Dict" begin + # svi = SimpleVarInfo(Dict(@varname(m) => 1.0)) + # @test getlogjoint(svi) == 0.0 + # @test haskey(svi, @varname(m)) + # @test !haskey(svi, @varname(m[1])) + # + # svi = SimpleVarInfo(Dict(@varname(m) => [1.0])) + # @test getlogjoint(svi) == 0.0 + # @test haskey(svi, @varname(m)) + # @test haskey(svi, @varname(m[1])) + # @test !haskey(svi, @varname(m[2])) + # @test svi[@varname(m)][1] == svi[@varname(m[1])] + # + # svi = SimpleVarInfo(Dict(@varname(m) => (a=[1.0],))) + # @test haskey(svi, @varname(m)) + # @test haskey(svi, @varname(m.a)) + # @test haskey(svi, @varname(m.a[1])) + # @test !haskey(svi, @varname(m.a[2])) + # @test !haskey(svi, @varname(m.a.b)) + # + # svi = SimpleVarInfo(Dict(@varname(m.a) => [1.0])) + # # Now we only have a variable `m.a` which is subsumed by `m`, + # # but we can't guarantee that we have the "entire" `m`. + # @test !haskey(svi, @varname(m)) + # @test haskey(svi, @varname(m.a)) + # @test haskey(svi, @varname(m.a[1])) + # @test !haskey(svi, @varname(m.a[2])) + # @test !haskey(svi, @varname(m.a.b)) + # end + # + # @testset "VarNamedVector" begin + # svi = SimpleVarInfo(push!!(DynamicPPL.VarNamedVector(), @varname(m) => 1.0)) + # @test getlogjoint(svi) == 0.0 + # @test haskey(svi, @varname(m)) + # @test !haskey(svi, @varname(m[1])) + # + # svi = SimpleVarInfo(push!!(DynamicPPL.VarNamedVector(), @varname(m) => [1.0])) + # @test getlogjoint(svi) == 0.0 + # @test haskey(svi, @varname(m)) + # @test haskey(svi, @varname(m[1])) + # @test !haskey(svi, @varname(m[2])) + # @test svi[@varname(m)][1] == svi[@varname(m[1])] + # + # svi = SimpleVarInfo(push!!(DynamicPPL.VarNamedVector(), @varname(m.a) => [1.0])) + # @test haskey(svi, @varname(m)) + # @test haskey(svi, @varname(m.a)) + # @test haskey(svi, @varname(m.a[1])) + # @test !haskey(svi, @varname(m.a[2])) + # @test !haskey(svi, @varname(m.a.b)) + # # The implementation of haskey and getvalue fo VarNamedVector is incomplete, the + # # next test is here to remind of us that. + # svi = SimpleVarInfo( + # push!!(DynamicPPL.VarNamedVector(), @varname(m.a.b) => [1.0]) + # ) + # @test_broken !haskey(svi, @varname(m.a.b.c.d)) + # end + # end @testset "link!! & invlink!! on $(nameof(model))" for model in - DynamicPPL.TestUtils.DEMO_MODELS + DynamicPPL.TestUtils.ALL_MODELS values_constrained = DynamicPPL.TestUtils.rand_prior_true(model) - @testset "$name" for (name, vi) in ( - ("SVI{Dict}", SimpleVarInfo(Dict{VarName,Any}())), - ("SVI{NamedTuple}", SimpleVarInfo(values_constrained)), - ("SVI{VNV}", SimpleVarInfo(DynamicPPL.VarNamedVector())), - ("TypedVarInfo", DynamicPPL.typed_varinfo(model)), - ) + @testset "$name" for (name, vi) in + (("SVI{Dict}", SimpleVarInfo(OrderedDict{VarName,Any}())), + # ("SVI{NamedTuple}", SimpleVarInfo(values_constrained)), + # ("SVI{VNV}", SimpleVarInfo(DynamicPPL.VarNamedVector())), + # ("TypedVarInfo", DynamicPPL.typed_varinfo(model)), +) + if name == "SVI{NamedTuple}" && + model.f === DynamicPPL.TestUtils.demo_one_variable_multiple_constraints + # TODO(mhauru) There's a bug in SimpleVarInfo{<:NamedTuple} for cases where + # a variable set with IndexLenses changes dimension under linking. This + # makes the link!! call crash. The below call to @test just marks the fact + # that there's something broken here. + @test false broken = true + continue + end for vn in DynamicPPL.TestUtils.varnames(model) vi = DynamicPPL.setindex!!(vi, get(values_constrained, vn), vn) end @@ -109,212 +118,225 @@ ) # `link!!` + @show vi[:] vi_linked = link!!(deepcopy(vi), model) - lp_unlinked = getlogjoint(vi_linked) + @show vi_linked[:] + # lp_unlinked = getlogjoint(vi_linked) lp_linked = getlogjoint_internal(vi_linked) + @show vi + @show vi_linked + @show lp_linked lp_linked_true @test lp_linked ≈ lp_linked_true - @test lp_unlinked ≈ lp_unlinked_true - @test logjoint(model, vi_linked) ≈ lp_unlinked - - # `invlink!!` - vi_invlinked = invlink!!(deepcopy(vi_linked), model) - lp_unlinked = getlogjoint(vi_invlinked) - also_lp_unlinked = getlogjoint_internal(vi_invlinked) - @test lp_unlinked ≈ lp_unlinked_true - @test also_lp_unlinked ≈ lp_unlinked_true - @test logjoint(model, vi_invlinked) ≈ lp_unlinked - - # Should result in same values. - @test all( - DynamicPPL.tovec(DynamicPPL.getindex_internal(vi_invlinked, vn)) ≈ - DynamicPPL.tovec(get(values_constrained, vn)) for - vn in DynamicPPL.TestUtils.varnames(model) - ) + # @test lp_unlinked ≈ lp_unlinked_true + # @test logjoint(model, vi_linked) ≈ lp_unlinked + # + # # `invlink!!` + # vi_invlinked = invlink!!(deepcopy(vi_linked), model) + # lp_unlinked = getlogjoint(vi_invlinked) + # also_lp_unlinked = getlogjoint_internal(vi_invlinked) + # @test lp_unlinked ≈ lp_unlinked_true + # @test also_lp_unlinked ≈ lp_unlinked_true + # @test logjoint(model, vi_invlinked) ≈ lp_unlinked + # + # # Should result in same values. + # @test all( + # DynamicPPL.tovec(DynamicPPL.getindex_internal(vi_invlinked, vn)) ≈ + # DynamicPPL.tovec(get(values_constrained, vn)) for + # vn in DynamicPPL.TestUtils.varnames(model) + # ) end end - @testset "SimpleVarInfo on $(nameof(model))" for model in - DynamicPPL.TestUtils.DEMO_MODELS - # We might need to pre-allocate for the variable `m`, so we need - # to see whether this is the case. - svi_nt = SimpleVarInfo(DynamicPPL.TestUtils.rand_prior_true(model)) - svi_dict = SimpleVarInfo(VarInfo(model), Dict) - vnv = DynamicPPL.VarNamedVector() - for (k, v) in pairs(DynamicPPL.TestUtils.rand_prior_true(model)) - vnv = push!!(vnv, VarName{k}() => v) - end - svi_vnv = SimpleVarInfo(vnv) - - @testset "$name" for (name, svi) in ( - ("NamedTuple", svi_nt), - ("Dict", svi_dict), - ("VarNamedVector", svi_vnv), - # TODO(mhauru) Fix linked SimpleVarInfos to work with our test models. - # DynamicPPL.set_transformed!!(deepcopy(svi_nt), true), - # DynamicPPL.set_transformed!!(deepcopy(svi_dict), true), - # DynamicPPL.set_transformed!!(deepcopy(svi_vnv), true), - ) - # Random seed is set in each `@testset`, so we need to sample - # a new realization for `m` here. - retval = model() - - ### Sampling ### - # Sample a new varinfo! - _, svi_new = DynamicPPL.init!!(model, svi) - - # Realization for `m` should be different wp. 1. - for vn in DynamicPPL.TestUtils.varnames(model) - @test svi_new[vn] != get(retval, vn) - end - - # Logjoint should be non-zero wp. 1. - @test getlogjoint(svi_new) != 0 - - ### Evaluation ### - values_eval_constrained = DynamicPPL.TestUtils.rand_prior_true(model) - if DynamicPPL.is_transformed(svi) - _values_prior, logpri_true = DynamicPPL.TestUtils.logprior_true_with_logabsdet_jacobian( - model, values_eval_constrained... - ) - values_eval, logπ_true = DynamicPPL.TestUtils.logjoint_true_with_logabsdet_jacobian( - model, values_eval_constrained... - ) - # Make sure that these two computation paths provide the same - # transformed values. - @test values_eval == _values_prior - else - logpri_true = DynamicPPL.TestUtils.logprior_true( - model, values_eval_constrained... - ) - logπ_true = DynamicPPL.TestUtils.logjoint_true( - model, values_eval_constrained... - ) - values_eval = values_eval_constrained - end - - # No logabsdet-jacobian correction needed for the likelihood. - loglik_true = DynamicPPL.TestUtils.loglikelihood_true( - model, values_eval_constrained... - ) - - # Update the realizations in `svi_new`. - svi_eval = svi_new - for vn in DynamicPPL.TestUtils.varnames(model) - svi_eval = DynamicPPL.setindex!!(svi_eval, get(values_eval, vn), vn) - end - - # Reset the logp accumulators. - svi_eval = DynamicPPL.resetaccs!!(svi_eval) - - # Compute `logjoint` using the varinfo. - logπ = logjoint(model, svi_eval) - logpri = logprior(model, svi_eval) - loglik = loglikelihood(model, svi_eval) - - # Values should not have changed. - for vn in DynamicPPL.TestUtils.varnames(model) - @test svi_eval[vn] == get(values_eval, vn) - end - - # Compare log-probability computations. - @test logpri ≈ logpri_true - @test loglik ≈ loglik_true - @test logπ ≈ logπ_true - end - end - - @testset "Dynamic constraints" begin - model = DynamicPPL.TestUtils.demo_dynamic_constraint() - - # Initialize. - svi_nt = DynamicPPL.set_transformed!!(SimpleVarInfo(), true) - svi_nt = last(DynamicPPL.init!!(model, svi_nt)) - svi_vnv = DynamicPPL.set_transformed!!( - SimpleVarInfo(DynamicPPL.VarNamedVector()), true - ) - svi_vnv = last(DynamicPPL.init!!(model, svi_vnv)) - - for svi in (svi_nt, svi_vnv) - # Sample with large variations in unconstrained space. - for i in 1:10 - for vn in keys(svi) - svi = DynamicPPL.setindex!!(svi, 10 * randn(), vn) - end - retval, svi = DynamicPPL.evaluate!!(model, svi) - @test retval.m == svi[@varname(m)] # `m` is unconstrained - @test retval.x ≠ svi[@varname(x)] # `x` is constrained depending on `m` - - retval_unconstrained, lp_true = DynamicPPL.TestUtils.logjoint_true_with_logabsdet_jacobian( - model, retval.m, retval.x - ) - - # Realizations from model should all be equal to the unconstrained realization. - for vn in DynamicPPL.TestUtils.varnames(model) - @test get(retval_unconstrained, vn) ≈ svi[vn] rtol = 1e-6 - end - - # `getlogp` should be equal to the logjoint with log-absdet-jac correction. - lp = getlogjoint_internal(svi) - # needs higher atol because of https://github.com/TuringLang/Bijectors.jl/issues/375 - @test lp ≈ lp_true atol = 1.2e-5 - end - end - end - - @testset "Static transformation" begin - model = DynamicPPL.TestUtils.demo_static_transformation() - - varinfos = DynamicPPL.TestUtils.setup_varinfos( - model, DynamicPPL.TestUtils.rand_prior_true(model), [@varname(s), @varname(m)] - ) - @testset "$(short_varinfo_name(vi))" for vi in varinfos - # Initialize varinfo and link. - vi_linked = DynamicPPL.link!!(vi, model) - - # Make sure `maybe_invlink_before_eval!!` results in `invlink!!`. - @test !DynamicPPL.is_transformed( - DynamicPPL.maybe_invlink_before_eval!!(deepcopy(vi), model) - ) - - # Resulting varinfo should no longer be transformed. - vi_result = last(DynamicPPL.init!!(model, deepcopy(vi))) - @test !DynamicPPL.is_transformed(vi_result) - - # Set the values to something that is out of domain if we're in constrained space. - for vn in keys(vi) - vi_linked = DynamicPPL.setindex!!(vi_linked, -rand(), vn) - end - - # NOTE: Evaluating a linked VarInfo, **specifically when the transformation - # is static**, will result in an invlinked VarInfo. This is because of - # `maybe_invlink_before_eval!`, which only invlinks if the transformation - # is static. (src/abstract_varinfo.jl) - retval, vi_unlinked_again = DynamicPPL.evaluate!!(model, deepcopy(vi_linked)) - - @test DynamicPPL.tovec(DynamicPPL.getindex_internal(vi_linked, @varname(s))) ≠ - DynamicPPL.tovec(retval.s) # `s` is unconstrained in original - @test DynamicPPL.tovec( - DynamicPPL.getindex_internal(vi_unlinked_again, @varname(s)) - ) == DynamicPPL.tovec(retval.s) # `s` is constrained in result - - # `m` should not be transformed. - @test vi_linked[@varname(m)] == retval.m - @test vi_unlinked_again[@varname(m)] == retval.m - - # Get ground truths - retval_unconstrained, lp_linked_true = DynamicPPL.TestUtils.logjoint_true_with_logabsdet_jacobian( - model, retval.s, retval.m - ) - lp_unlinked_true = DynamicPPL.TestUtils.logjoint_true(model, retval.s, retval.m) - - @test DynamicPPL.tovec(DynamicPPL.getindex_internal(vi_linked, @varname(s))) ≈ - DynamicPPL.tovec(retval_unconstrained.s) - @test DynamicPPL.tovec(DynamicPPL.getindex_internal(vi_linked, @varname(m))) ≈ - DynamicPPL.tovec(retval_unconstrained.m) - - # The unlinked varinfo should hold the unlinked logp. - lp_unlinked = getlogjoint(vi_unlinked_again) - @test getlogjoint(vi_unlinked_again) ≈ lp_unlinked_true - end - end + # @testset "SimpleVarInfo on $(nameof(model))" for model in + # DynamicPPL.TestUtils.ALL_MODELS + # # We might need to pre-allocate for the variable `m`, so we need + # # to see whether this is the case. + # svi_nt = SimpleVarInfo(DynamicPPL.TestUtils.rand_prior_true(model)) + # svi_dict = SimpleVarInfo(VarInfo(model), Dict) + # vnv = DynamicPPL.VarNamedVector() + # for (k, v) in pairs(DynamicPPL.TestUtils.rand_prior_true(model)) + # vnv = push!!(vnv, VarName{k}() => v) + # end + # svi_vnv = SimpleVarInfo(vnv) + # + # @testset "$name" for (name, svi) in ( + # ("NamedTuple", svi_nt), + # ("Dict", svi_dict), + # ("VarNamedVector", svi_vnv), + # # TODO(mhauru) Fix linked SimpleVarInfos to work with our test models. + # # DynamicPPL.set_transformed!!(deepcopy(svi_nt), true), + # # DynamicPPL.set_transformed!!(deepcopy(svi_dict), true), + # # DynamicPPL.set_transformed!!(deepcopy(svi_vnv), true), + # ) + # # Random seed is set in each `@testset`, so we need to sample + # # a new realization for `m` here. + # retval = model() + # + # ### Sampling ### + # # Sample a new varinfo! + # _, svi_new = DynamicPPL.init!!(model, svi) + # + # # Realization for `m` should be different wp. 1. + # for vn in DynamicPPL.TestUtils.varnames(model) + # @test svi_new[vn] != get(retval, vn) + # end + # + # # Logjoint should be non-zero wp. 1. + # @test getlogjoint(svi_new) != 0 + # + # ### Evaluation ### + # values_eval_constrained = DynamicPPL.TestUtils.rand_prior_true(model) + # if DynamicPPL.is_transformed(svi) + # _values_prior, logpri_true = DynamicPPL.TestUtils.logprior_true_with_logabsdet_jacobian( + # model, values_eval_constrained... + # ) + # values_eval, logπ_true = DynamicPPL.TestUtils.logjoint_true_with_logabsdet_jacobian( + # model, values_eval_constrained... + # ) + # # Make sure that these two computation paths provide the same + # # transformed values. + # @test values_eval == _values_prior + # else + # logpri_true = DynamicPPL.TestUtils.logprior_true( + # model, values_eval_constrained... + # ) + # logπ_true = DynamicPPL.TestUtils.logjoint_true( + # model, values_eval_constrained... + # ) + # values_eval = values_eval_constrained + # end + # + # # No logabsdet-jacobian correction needed for the likelihood. + # loglik_true = DynamicPPL.TestUtils.loglikelihood_true( + # model, values_eval_constrained... + # ) + # + # # Update the realizations in `svi_new`. + # svi_eval = svi_new + # for vn in DynamicPPL.TestUtils.varnames(model) + # svi_eval = DynamicPPL.setindex!!(svi_eval, get(values_eval, vn), vn) + # end + # + # # Reset the logp accumulators. + # svi_eval = DynamicPPL.resetaccs!!(svi_eval) + # + # # Compute `logjoint` using the varinfo. + # logπ = logjoint(model, svi_eval) + # logpri = logprior(model, svi_eval) + # loglik = loglikelihood(model, svi_eval) + # + # # Values should not have changed. + # for vn in DynamicPPL.TestUtils.varnames(model) + # # TODO(mhauru) Workaround for + # # https://github.com/JuliaLang/LinearAlgebra.jl/pull/1404 + # # Remove once the fix is all Julia versions we support. + # val = get(values_eval, vn) + # if val isa Cholesky + # @test svi_eval[vn].L == val.L + # else + # @test svi_eval[vn] == val + # end + # end + # + # # Compare log-probability computations. + # @test logpri ≈ logpri_true + # @test loglik ≈ loglik_true + # @test logπ ≈ logπ_true + # end + # end + # + # @testset "Dynamic constraints" begin + # model = DynamicPPL.TestUtils.demo_dynamic_constraint() + # + # # Initialize. + # svi_nt = DynamicPPL.set_transformed!!(SimpleVarInfo(), true) + # svi_nt = last(DynamicPPL.init!!(model, svi_nt)) + # svi_vnv = DynamicPPL.set_transformed!!( + # SimpleVarInfo(DynamicPPL.VarNamedVector()), true + # ) + # svi_vnv = last(DynamicPPL.init!!(model, svi_vnv)) + # + # for svi in (svi_nt, svi_vnv) + # # Sample with large variations in unconstrained space. + # for i in 1:10 + # for vn in keys(svi) + # svi = DynamicPPL.setindex!!(svi, 10 * randn(), vn) + # end + # retval, svi = DynamicPPL.evaluate!!(model, svi) + # @test retval.m == svi[@varname(m)] # `m` is unconstrained + # @test retval.x ≠ svi[@varname(x)] # `x` is constrained depending on `m` + # + # retval_unconstrained, lp_true = DynamicPPL.TestUtils.logjoint_true_with_logabsdet_jacobian( + # model, retval.m, retval.x + # ) + # + # # Realizations from model should all be equal to the unconstrained realization. + # for vn in DynamicPPL.TestUtils.varnames(model) + # @test get(retval_unconstrained, vn) ≈ svi[vn] rtol = 1e-6 + # end + # + # # `getlogp` should be equal to the logjoint with log-absdet-jac correction. + # lp = getlogjoint_internal(svi) + # # needs higher atol because of https://github.com/TuringLang/Bijectors.jl/issues/375 + # @test lp ≈ lp_true atol = 1.2e-5 + # end + # end + # end + # + # @testset "Static transformation" begin + # model = DynamicPPL.TestUtils.demo_static_transformation() + # + # varinfos = DynamicPPL.TestUtils.setup_varinfos( + # model, DynamicPPL.TestUtils.rand_prior_true(model), [@varname(s), @varname(m)] + # ) + # @testset "$(short_varinfo_name(vi))" for vi in varinfos + # # Initialize varinfo and link. + # vi_linked = DynamicPPL.link!!(vi, model) + # + # # Make sure `maybe_invlink_before_eval!!` results in `invlink!!`. + # @test !DynamicPPL.is_transformed( + # DynamicPPL.maybe_invlink_before_eval!!(deepcopy(vi), model) + # ) + # + # # Resulting varinfo should no longer be transformed. + # vi_result = last(DynamicPPL.init!!(model, deepcopy(vi))) + # @test !DynamicPPL.is_transformed(vi_result) + # + # # Set the values to something that is out of domain if we're in constrained space. + # for vn in keys(vi) + # vi_linked = DynamicPPL.setindex!!(vi_linked, -rand(), vn) + # end + # + # # NOTE: Evaluating a linked VarInfo, **specifically when the transformation + # # is static**, will result in an invlinked VarInfo. This is because of + # # `maybe_invlink_before_eval!`, which only invlinks if the transformation + # # is static. (src/abstract_varinfo.jl) + # retval, vi_unlinked_again = DynamicPPL.evaluate!!(model, deepcopy(vi_linked)) + # + # @test DynamicPPL.tovec(DynamicPPL.getindex_internal(vi_linked, @varname(s))) ≠ + # DynamicPPL.tovec(retval.s) # `s` is unconstrained in original + # @test DynamicPPL.tovec( + # DynamicPPL.getindex_internal(vi_unlinked_again, @varname(s)) + # ) == DynamicPPL.tovec(retval.s) # `s` is constrained in result + # + # # `m` should not be transformed. + # @test vi_linked[@varname(m)] == retval.m + # @test vi_unlinked_again[@varname(m)] == retval.m + # + # # Get ground truths + # retval_unconstrained, lp_linked_true = DynamicPPL.TestUtils.logjoint_true_with_logabsdet_jacobian( + # model, retval.s, retval.m + # ) + # lp_unlinked_true = DynamicPPL.TestUtils.logjoint_true(model, retval.s, retval.m) + # + # @test DynamicPPL.tovec(DynamicPPL.getindex_internal(vi_linked, @varname(s))) ≈ + # DynamicPPL.tovec(retval_unconstrained.s) + # @test DynamicPPL.tovec(DynamicPPL.getindex_internal(vi_linked, @varname(m))) ≈ + # DynamicPPL.tovec(retval_unconstrained.m) + # + # # The unlinked varinfo should hold the unlinked logp. + # lp_unlinked = getlogjoint(vi_unlinked_again) + # @test getlogjoint(vi_unlinked_again) ≈ lp_unlinked_true + # end + # end end diff --git a/test/varinfo.jl b/test/varinfo.jl index 0d0ddc15d..2a83e2f05 100644 --- a/test/varinfo.jl +++ b/test/varinfo.jl @@ -318,7 +318,7 @@ end end @testset "returned on MCMCChains.Chains" begin - @testset "$(model.f)" for model in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(model.f)" for model in DynamicPPL.TestUtils.ALL_MODELS chain = make_chain_from_prior(model, 10) # A simple way of checking that the computation is determinstic: run twice and compare. res1 = returned(model, MCMCChains.get_sections(chain, :parameters)) @@ -460,7 +460,7 @@ end end @testset "values_as" begin - @testset "$(nameof(model))" for model in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(nameof(model))" for model in DynamicPPL.TestUtils.ALL_MODELS example_values = DynamicPPL.TestUtils.rand_prior_true(model) vns = DynamicPPL.TestUtils.varnames(model) @@ -730,7 +730,7 @@ end end @testset "merge" begin - @testset "$(model.f)" for model in DynamicPPL.TestUtils.DEMO_MODELS + @testset "$(model.f)" for model in DynamicPPL.TestUtils.ALL_MODELS vns = DynamicPPL.TestUtils.varnames(model) varinfos = DynamicPPL.TestUtils.setup_varinfos( model, @@ -825,7 +825,7 @@ end end @testset "issue #842" begin - model = DynamicPPL.TestUtils.DEMO_MODELS[1] + model = DynamicPPL.TestUtils.demo_dot_assume_observe() varinfo = VarInfo(model) n = length(varinfo[:]) diff --git a/test/varnamedvector.jl b/test/varnamedvector.jl index 3a327c147..9a4ef12c3 100644 --- a/test/varnamedvector.jl +++ b/test/varnamedvector.jl @@ -667,7 +667,7 @@ end end @testset "VarInfo + VarNamedVector" begin - models = DynamicPPL.TestUtils.DEMO_MODELS + models = DynamicPPL.TestUtils.ALL_MODELS @testset "$(model.f)" for model in models # NOTE: Need to set random seed explicitly to avoid using the same seed # for initialization as for sampling in the inner testset below.