diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index a5735402a..000000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: CI - -on: - push: - branches: - - main - - backport-* - 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: ${{ matrix.runner.os }} - strategy: - fail-fast: false - - matrix: - runner: - # Current stable version - - 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 - - version: '1' - os: windows-latest - num_threads: 2 - # macOS - - version: '1' - os: macos-latest - num_threads: 2 - test_group: - - Group1 - - Group2 - - steps: - - uses: actions/checkout@v5 - - - uses: julia-actions/setup-julia@v2 - with: - version: ${{ matrix.runner.version }} - - - uses: julia-actions/cache@v2 - - - uses: julia-actions/julia-buildpkg@v1 - - - uses: julia-actions/julia-runtest@v1 - env: - GROUP: ${{ matrix.test_group }} - JULIA_NUM_THREADS: ${{ matrix.runner.num_threads }} - # Only run Aqua tests on latest version - AQUA: ${{ matrix.runner.version == '1' && 'true' || 'false' }} - - - uses: julia-actions/julia-processcoverage@v1 - - - uses: codecov/codecov-action@v5 - with: - files: lcov.info - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: true diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml deleted file mode 100644 index 0c1c69aca..000000000 --- a/.github/workflows/CompatHelper.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: CompatHelper - -on: - schedule: - - cron: '00 00 * * *' - workflow_dispatch: -jobs: - CompatHelper: - runs-on: ubuntu-latest - steps: - - name: Pkg.add("CompatHelper") - run: julia -e 'using Pkg; Pkg.add("CompatHelper")' - - name: CompatHelper.main() - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} - run: julia -e 'using CompatHelper; CompatHelper.main(; subdirs = ["", "docs", "test", "benchmarks"])' diff --git a/.github/workflows/DocTest.yml b/.github/workflows/DocTest.yml deleted file mode 100644 index 43333d6f8..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@v5 - - - 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/DocsNav.yml b/.github/workflows/DocsNav.yml deleted file mode 100644 index 4a0f04577..000000000 --- a/.github/workflows/DocsNav.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Rebuild docs with newest navbar - -on: - # 3:25 AM UTC every Sunday -- choose an uncommon time to avoid - # periods of heavy GitHub Actions usage - schedule: - - cron: '25 3 * * 0' - # Whenever needed - workflow_dispatch: - -permissions: - contents: write - -jobs: - update-navbar: - runs-on: ubuntu-latest - - steps: - - name: Checkout gh-pages branch - uses: actions/checkout@v5 - with: - ref: gh-pages - - - name: Insert navbar - uses: TuringLang/actions/DocsNav@main - with: - doc-path: '.' - - - name: Commit and push changes - run: | - if [[ -n $(git status -s) ]]; then - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "GitHub Actions" - git add -A - git commit -m "Update navbar (automated)" - git push - else - echo "No changes to commit" - fi diff --git a/.github/workflows/DocsPreviewCleanup.yml b/.github/workflows/DocsPreviewCleanup.yml deleted file mode 100644 index 7c3a76174..000000000 --- a/.github/workflows/DocsPreviewCleanup.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: DocsPreviewCleanup - -on: - pull_request: - types: [closed] - -jobs: - cleanup: - runs-on: ubuntu-latest - steps: - - name: Checkout gh-pages branch - uses: actions/checkout@v5 - with: - ref: gh-pages - - name: Delete preview and history + push changes - run: | - if [ -d "previews/PR$PRNUM" ]; then - git config user.name "Documenter.jl" - git config user.email "documenter@juliadocs.github.io" - git rm -rf "previews/PR$PRNUM" - git commit -m "delete preview" - git branch gh-pages-new $(echo "delete history" | git commit-tree HEAD^{tree}) - git push --force origin gh-pages-new:gh-pages - fi - env: - PRNUM: ${{ github.event.number }} diff --git a/.github/workflows/Enzyme.yml b/.github/workflows/Enzyme.yml deleted file mode 100644 index 36f11b914..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@v5 - - - 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/.github/workflows/IntegrationTest.yml b/.github/workflows/IntegrationTest.yml deleted file mode 100644 index 5ff49254c..000000000 --- a/.github/workflows/IntegrationTest.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: IntegrationTest - -on: - push: - branches: - - main - merge_group: - types: [checks_requested] - pull_request: - branches: [main] - types: [auto_merge_enabled] - -jobs: - test: - name: ${{ matrix.package.repo }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - package: - - {user: TuringLang, repo: Turing.jl} - - steps: - - uses: actions/checkout@v5 - - - uses: julia-actions/setup-julia@v2 - with: - version: 1 - - - uses: julia-actions/julia-buildpkg@v1 - - - name: Clone Downstream - uses: actions/checkout@v5 - with: - repository: ${{ matrix.package.user }}/${{ matrix.package.repo }} - path: downstream - - - name: Determine Julia version - shell: bash - id: julia-version - run: | - echo "julia=$(julia --version | cut -d' ' -f3)" >> $GITHUB_OUTPUT - - - name: Load this and run the downstream tests - shell: julia --color=yes --project=downstream {0} - run: | - using Pkg - try - # force it to use this PR's version of the package - Pkg.develop(PackageSpec(path=".")) # resolver may fail with main deps - Pkg.update() - Pkg.test(julia_args=["--depwarn=no"]) # resolver may fail with test time deps - catch err - err isa Pkg.Resolve.ResolverError || rethrow() - # If we can't resolve that means this is incompatible by SemVer and this is fine - # It means we marked this as a breaking change, so we don't need to worry about - # Mistakenly introducing a breaking change, as we have intentionally made one - @info "Not compatible with this release. No problem." exception=err - exit(0) # Exit immediately, as a success - end diff --git a/.github/workflows/JuliaPre.yml b/.github/workflows/JuliaPre.yml deleted file mode 100644 index ab8998d0a..000000000 --- a/.github/workflows/JuliaPre.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: JuliaPre - -# JuliaPre tests are currently disabled because there is no pre-release of v1.13. -on: workflow_dispatch - # push: - # branches: - # - main - # pull_request: - -# needed to allow julia-actions/cache to delete old caches that it has created -permissions: - actions: write - contents: read - -jobs: - test: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - test_group: - - Group1 - - Group2 - - steps: - - uses: actions/checkout@v5 - - uses: julia-actions/setup-julia@v2 - with: - version: 'pre' # pre-release - - uses: julia-actions/cache@v2 - - uses: julia-actions/julia-buildpkg@v1 - - uses: julia-actions/julia-runtest@v1 - env: - GROUP: ${{ matrix.test_group }} diff --git a/.github/workflows/PRAssign.yml b/.github/workflows/PRAssign.yml deleted file mode 100644 index 12ec2ff25..000000000 --- a/.github/workflows/PRAssign.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Automatically assign PR authors - -on: - pull_request_target: - types: - - opened - -permissions: - pull-requests: write - -jobs: - assign-author: - runs-on: ubuntu-latest - steps: - - uses: TuringLang/actions/PRAssign@main diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml deleted file mode 100644 index f49313b66..000000000 --- a/.github/workflows/TagBot.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: TagBot -on: - issue_comment: - types: - - created - workflow_dispatch: -jobs: - TagBot: - if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' - runs-on: ubuntu-latest - steps: - - uses: JuliaRegistries/TagBot@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - ssh: ${{ secrets.DOCUMENTER_KEY }} diff --git a/src/contexts/init.jl b/src/contexts/init.jl index a79969a13..fd3d5ef3e 100644 --- a/src/contexts/init.jl +++ b/src/contexts/init.jl @@ -214,7 +214,7 @@ struct RangeAndLinked end """ - VectorWithRanges( + VectorWithRanges{Tlink}( iden_varname_ranges::NamedTuple, varname_ranges::Dict{VarName,RangeAndLinked}, vect::AbstractVector{<:Real}, @@ -231,13 +231,19 @@ non-identity-optic VarNames are stored in the `varname_ranges` Dict. It would be nice to improve the NamedTuple and Dict approach. See, e.g. https://github.com/TuringLang/DynamicPPL.jl/issues/1116. """ -struct VectorWithRanges{N<:NamedTuple,T<:AbstractVector{<:Real}} +struct VectorWithRanges{Tlink,N<:NamedTuple,T<:AbstractVector{<:Real}} # This NamedTuple stores the ranges for identity VarNames iden_varname_ranges::N # This Dict stores the ranges for all other VarNames varname_ranges::Dict{VarName,RangeAndLinked} # The full parameter vector which we index into to get variable values vect::T + + function VectorWithRanges{Tlink}( + iden_varname_ranges::N, varname_ranges::Dict{VarName,RangeAndLinked}, vect::T + ) where {Tlink,N,T} + return new{Tlink,N,T}(iden_varname_ranges, varname_ranges, vect) + end end function _get_range_and_linked( @@ -252,7 +258,29 @@ function init( ::Random.AbstractRNG, vn::VarName, dist::Distribution, - p::InitFromParams{<:VectorWithRanges}, + p::InitFromParams{<:VectorWithRanges{true}}, +) + vr = p.params + range_and_linked = _get_range_and_linked(vr, vn) + transform = from_linked_vec_transform(dist) + return (@view vr.vect[range_and_linked.range]), transform +end +function init( + ::Random.AbstractRNG, + vn::VarName, + dist::Distribution, + p::InitFromParams{<:VectorWithRanges{false}}, +) + vr = p.params + range_and_linked = _get_range_and_linked(vr, vn) + transform = from_vec_transform(dist) + return (@view vr.vect[range_and_linked.range]), transform +end +function init( + ::Random.AbstractRNG, + vn::VarName, + dist::Distribution, + p::InitFromParams{<:VectorWithRanges{nothing}}, ) vr = p.params range_and_linked = _get_range_and_linked(vr, vn) diff --git a/src/fasteval.jl b/src/fasteval.jl index 4f402f4a8..d98002201 100644 --- a/src/fasteval.jl +++ b/src/fasteval.jl @@ -1,6 +1,8 @@ using DynamicPPL: + DynamicPPL, AbstractVarInfo, AccumulatorTuple, + AbstractInitStrategy, InitContext, InitFromParams, LogJacobianAccumulator, @@ -28,6 +30,65 @@ using LogDensityProblems: LogDensityProblems import DifferentiationInterface as DI using Random: Random +""" + DynamicPPL.fast_evaluate!!( + [rng::Random.AbstractRNG,] + model::Model, + strategy::AbstractInitStrategy, + accs::AccumulatorTuple, params::AbstractVector{<:Real} + ) + +Evaluate a model using parameters obtained via `strategy`, and only computing the results in +the provided accumulators. + +It is assumed that the accumulators passed in have been initialised to appropriate values, +as this function will not reset them. The default constructors for each accumulator will do +this for you correctly. + +Returns a tuple of the model's return value, plus an `OnlyAccsVarInfo`. Note that the `accs` +argument may be mutated (depending on how the accumulators are implemented); hence the `!!` +in the function name. +""" +@inline function fast_evaluate!!( + # Note that this `@inline` is mandatory for performance. If it's not inlined, it leads + # to extra allocations (even for trivial models) and much slower runtime. + rng::Random.AbstractRNG, + model::Model, + strategy::AbstractInitStrategy, + accs::AccumulatorTuple, +) + ctx = InitContext(rng, strategy) + model = DynamicPPL.setleafcontext(model, ctx) + # Calling `evaluate!!` would be fine, but would lead to an extra call to resetaccs!!, + # which is unnecessary. So we shortcircuit this by simply calling `_evaluate!!` + # directly. To preserve thread-safety we need to reproduce the ThreadSafeVarInfo logic + # here. + # TODO(penelopeysm): This should _not_ check Threads.nthreads(). I still don't know what + # it _should_ do, but this is wrong regardless. + # https://github.com/TuringLang/DynamicPPL.jl/issues/1086 + return if Threads.nthreads() > 1 + # WARNING: Do NOT move get_param_eltype(strategy) into an intermediate variable, it + # will cause type instabilities! See also unflatten in src/varinfo.jl. + accs = map(accs) do acc + DynamicPPL.convert_eltype( + float_type_with_fallback(DynamicPPL.get_param_eltype(strategy)), acc + ) + end + tsvi = ThreadSafeVarInfo(OnlyAccsVarInfo(accs)) + retval, tsvi_new = DynamicPPL._evaluate!!(model, tsvi) + retval, DynamicPPL.setaccs!!(tsvi_new.varinfo, DynamicPPL.getaccs(tsvi_new)) + else + vi = OnlyAccsVarInfo(accs) + DynamicPPL._evaluate!!(model, vi) + end +end +@inline function fast_evaluate!!( + model::Model, strategy::AbstractInitStrategy, accs::AccumulatorTuple +) + # This `@inline` is also mandatory for performance + return fast_evaluate!!(Random.default_rng(), model, strategy, accs) +end + """ FastLDF( model::Model, @@ -137,6 +198,9 @@ general limitation of vectorised parameters: the original `unflatten` + `evaluat approach also fails with such models. """ struct FastLDF{ + # true if all variables are linked; false if all variables are unlinked; nothing if + # mixed + Tlink, M<:Model, AD<:Union{ADTypes.AbstractADType,Nothing}, F<:Function, @@ -160,6 +224,21 @@ struct FastLDF{ # Figure out which variable corresponds to which index, and # which variables are linked. all_iden_ranges, all_ranges = get_ranges_and_linked(varinfo) + # Figure out if all variables are linked, unlinked, or mixed + link_statuses = Bool[] + for ral in all_iden_ranges + push!(link_statuses, ral.is_linked) + end + for (_, ral) in all_ranges + push!(link_statuses, ral.is_linked) + end + Tlink = if all(link_statuses) + true + elseif all(!s for s in link_statuses) + false + else + nothing + end x = [val for val in varinfo[:]] dim = length(x) # Do AD prep if needed @@ -169,12 +248,13 @@ struct FastLDF{ # Make backend-specific tweaks to the adtype adtype = DynamicPPL.tweak_adtype(adtype, model, varinfo) DI.prepare_gradient( - FastLogDensityAt(model, getlogdensity, all_iden_ranges, all_ranges), + FastLogDensityAt{Tlink}(model, getlogdensity, all_iden_ranges, all_ranges), adtype, x, ) end return new{ + Tlink, typeof(model), typeof(adtype), typeof(getlogdensity), @@ -206,70 +286,58 @@ end fast_ldf_accs(::typeof(getlogprior)) = AccumulatorTuple((LogPriorAccumulator(),)) fast_ldf_accs(::typeof(getloglikelihood)) = AccumulatorTuple((LogLikelihoodAccumulator(),)) -struct FastLogDensityAt{M<:Model,F<:Function,N<:NamedTuple} +struct FastLogDensityAt{Tlink,M<:Model,F<:Function,N<:NamedTuple} model::M getlogdensity::F iden_varname_ranges::N varname_ranges::Dict{VarName,RangeAndLinked} + + function FastLogDensityAt{Tlink}( + model::M, + getlogdensity::F, + iden_varname_ranges::N, + varname_ranges::Dict{VarName,RangeAndLinked}, + ) where {Tlink,M,F,N} + return new{Tlink,M,F,N}(model, getlogdensity, iden_varname_ranges, varname_ranges) + end end -function (f::FastLogDensityAt)(params::AbstractVector{<:Real}) - ctx = InitContext( - Random.default_rng(), - InitFromParams( - VectorWithRanges(f.iden_varname_ranges, f.varname_ranges, params), nothing - ), +function (f::FastLogDensityAt{Tlink})(params::AbstractVector{<:Real}) where {Tlink} + strategy = InitFromParams( + VectorWithRanges{Tlink}(f.iden_varname_ranges, f.varname_ranges, params), nothing ) - model = DynamicPPL.setleafcontext(f.model, ctx) - accs = fast_ldf_accs(f.getlogdensity) - # Calling `evaluate!!` would be fine, but would lead to an extra call to resetaccs!!, - # which is unnecessary. So we shortcircuit this by simply calling `_evaluate!!` - # directly. To preserve thread-safety we need to reproduce the ThreadSafeVarInfo logic - # here. - # TODO(penelopeysm): This should _not_ check Threads.nthreads(). I still don't know what - # it _should_ do, but this is wrong regardless. - # https://github.com/TuringLang/DynamicPPL.jl/issues/1086 - vi = if Threads.nthreads() > 1 - accs = map( - acc -> DynamicPPL.convert_eltype(float_type_with_fallback(eltype(params)), acc), - accs, - ) - ThreadSafeVarInfo(OnlyAccsVarInfo(accs)) - else - OnlyAccsVarInfo(accs) - end - _, vi = DynamicPPL._evaluate!!(model, vi) + _, vi = fast_evaluate!!(f.model, strategy, fast_ldf_accs(f.getlogdensity)) return f.getlogdensity(vi) end -function LogDensityProblems.logdensity(fldf::FastLDF, params::AbstractVector{<:Real}) - return FastLogDensityAt( - fldf.model, fldf._getlogdensity, fldf._iden_varname_ranges, fldf._varname_ranges +function LogDensityProblems.logdensity( + ldf::FastLDF{Tlink}, params::AbstractVector{<:Real} +) where {Tlink} + return FastLogDensityAt{Tlink}( + ldf.model, ldf._getlogdensity, ldf._iden_varname_ranges, ldf._varname_ranges )( params ) end function LogDensityProblems.logdensity_and_gradient( - fldf::FastLDF, params::AbstractVector{<:Real} -) + ldf::FastLDF{Tlink}, params::AbstractVector{<:Real} +) where {Tlink} return DI.value_and_gradient( - FastLogDensityAt( - fldf.model, fldf._getlogdensity, fldf._iden_varname_ranges, fldf._varname_ranges + FastLogDensityAt{Tlink}( + ldf.model, ldf._getlogdensity, ldf._iden_varname_ranges, ldf._varname_ranges ), - fldf._adprep, - fldf.adtype, + ldf._adprep, + ldf.adtype, params, ) end -function LogDensityProblems.capabilities( - ::Type{<:DynamicPPL.Experimental.FastLDF{M,Nothing}} -) where {M} +function LogDensityProblems.capabilities(::Type{<:FastLDF{T,M,Nothing}}) where {T,M} return LogDensityProblems.LogDensityOrder{0}() end function LogDensityProblems.capabilities( - ::Type{<:DynamicPPL.Experimental.FastLDF{M,<:ADTypes.AbstractADType}} -) where {M} + ::Type{<:FastLDF{T,M,<:ADTypes.AbstractADType}} +) where {T,M} return LogDensityProblems.LogDensityOrder{1}() end function LogDensityProblems.dimension(fldf::FastLDF) diff --git a/src/test_utils/ad.jl b/src/test_utils/ad.jl index 8ee850877..87290aae8 100644 --- a/src/test_utils/ad.jl +++ b/src/test_utils/ad.jl @@ -271,7 +271,7 @@ function run_ad( # Calculate log-density and gradient with the backend of interest verbose && @info "Running AD on $(model.f) with $(adtype)\n" verbose && println(" params : $(params)") - ldf = LogDensityFunction(model, getlogdensity, varinfo; adtype=adtype) + ldf = DynamicPPL.Experimental.FastLDF(model, getlogdensity, varinfo; adtype=adtype) value, grad = logdensity_and_gradient(ldf, params) # collect(): https://github.com/JuliaDiff/DifferentiationInterface.jl/issues/754 @@ -288,7 +288,7 @@ function run_ad( value_true = test.value grad_true = test.grad elseif test isa WithBackend - ldf_reference = LogDensityFunction( + ldf_reference = DynamicPPL.Experimental.FastLDF( model, getlogdensity, varinfo; adtype=test.adtype ) value_true, grad_true = logdensity_and_gradient(ldf_reference, params) diff --git a/test/fasteval.jl b/test/fasteval.jl index db2333711..ab7e7e9c5 100644 --- a/test/fasteval.jl +++ b/test/fasteval.jl @@ -95,7 +95,23 @@ end end end -@testset "FastLDF: performance" begin +@testset "FastLDF: Type stability" begin + @testset "$(m.f)" for m in DynamicPPL.TestUtils.DEMO_MODELS + unlinked_vi = DynamicPPL.VarInfo(m) + @testset "$islinked" for islinked in (false, true) + vi = if islinked + DynamicPPL.link!!(unlinked_vi, m) + else + unlinked_vi + end + ldf = DynamicPPL.Experimental.FastLDF(m, DynamicPPL.getlogjoint_internal, vi) + x = vi[:] + @inferred LogDensityProblems.logdensity(ldf, x) + end + end +end + +@testset "Fast evaluation: performance" begin if Threads.nthreads() == 1 # Evaluating these three models should not lead to any allocations (but only when # not using TSVI).