From cf95e4b20140ad79c85ee8473372ef265403c969 Mon Sep 17 00:00:00 2001 From: Ryan Senne <50930199+rsenne@users.noreply.github.com> Date: Tue, 7 Jul 2026 08:36:59 -0400 Subject: [PATCH 1/5] Remove ForwardDiff trigger and default in DynamicPPLExt.jl --- Project.toml | 2 +- ext/DynamicPPLExt.jl | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index acf4ccf..82d250a 100644 --- a/Project.toml +++ b/Project.toml @@ -23,7 +23,7 @@ Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [extensions] -DynamicPPLExt = ["DynamicPPL", "FlexiChains", "ForwardDiff", "LogDensityProblems"] +DynamicPPLExt = ["DynamicPPL", "FlexiChains", "LogDensityProblems"] EnzymeExt = "Enzyme" LogDensityProblemsExt = "LogDensityProblems" diff --git a/ext/DynamicPPLExt.jl b/ext/DynamicPPLExt.jl index cc33ad4..bf0d2d3 100644 --- a/ext/DynamicPPLExt.jl +++ b/ext/DynamicPPLExt.jl @@ -8,7 +8,7 @@ using FlexiChains: FlexiChain, VarName, VNChain, SymChain using LogDensityProblems: LogDensityProblems """ - DensityModel(turing_model::DynamicPPL.Model; ad_backend=ADTypes.AutoForwardDiff(), hvp=nothing) + DensityModel(turing_model::DynamicPPL.Model; ad_backend, hvp=nothing) Convenience constructor: wraps a DynamicPPL/Turing `@model` directly as a `DensityModel`, automatically extracting parameter names and wiring up gradient @@ -19,22 +19,20 @@ triggers for this extension), plus any AD backend that is used. # Example ```julia -using Turing, ParallelMCMC, FlexiChains +using Turing, ParallelMCMC, FlexiChains, ForwardDiff @model function mymodel(y) μ ~ Normal(0, 1) y ~ Normal(μ, 0.5) end -# AutoForwardDiff is the default. For larger models pass an explicit backend -# and `using` the corresponding package (Enzyme, Mooncake). -model = DensityModel(mymodel(1.5)) +model = DensityModel(mymodel(1.5); ad_backend=AutoForwardDiff()) chain = sample(model, AdaptiveMALASampler(0.3; n_warmup=500), 2_000; chain_type=FlexiChains.VNChain, discard_warmup=true, progress=true) ``` """ function ParallelMCMC.DensityModel( - turing_model::DynamicPPL.Model; ad_backend=ADTypes.AutoForwardDiff(), hvp=nothing + turing_model::DynamicPPL.Model; ad_backend, hvp=nothing ) # Sample in linked/unconstrained space and let DynamicPPL provide the gradient. ld = DynamicPPL.LogDensityFunction( From a06bb8d4ccf691aca1c4ca2761e1cb15acd63bae Mon Sep 17 00:00:00 2001 From: Ryan Senne <50930199+rsenne@users.noreply.github.com> Date: Tue, 7 Jul 2026 08:41:29 -0400 Subject: [PATCH 2/5] Update Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd444a6..2b0e2a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Removed + +- `DynamicPPLExt` no longer requires `ForwardDiff` as a triggering library to load. + ## [0.2.0] - 2026-06-29 ### Changed From 889e3a7edfe6153a59cb53b7256afe36df83dc0a Mon Sep 17 00:00:00 2001 From: Ryan Senne <50930199+rsenne@users.noreply.github.com> Date: Tue, 7 Jul 2026 09:16:31 -0400 Subject: [PATCH 3/5] Fix tests --- test/test-DEER-Turing-Logistic.jl | 4 ++- test/test-Turing-Integration.jl | 50 +++++++++++++++---------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/test/test-DEER-Turing-Logistic.jl b/test/test-DEER-Turing-Logistic.jl index fcddcb0..caded78 100644 --- a/test/test-DEER-Turing-Logistic.jl +++ b/test/test-DEER-Turing-Logistic.jl @@ -68,7 +68,9 @@ end function _deer_logistic_turing_density_model() return DensityModel( - _deer_logistic_regression(_LR_X, _LR_y); hvp=(β, v) -> _hvp_lr(β, v, _LR_X, _LR_y) + _deer_logistic_regression(_LR_X, _LR_y); + ad_backend=ADTypes.AutoForwardDiff(), + hvp=(β, v) -> _hvp_lr(β, v, _LR_X, _LR_y), ) end diff --git a/test/test-Turing-Integration.jl b/test/test-Turing-Integration.jl index 0eb64e7..f6c0c69 100644 --- a/test/test-Turing-Integration.jl +++ b/test/test-Turing-Integration.jl @@ -70,7 +70,7 @@ end end @testset "DynamicPPLExt: convenience constructor" begin - model = DensityModel(normal_model(TRUE_OBS)) + model = DensityModel(normal_model(TRUE_OBS); ad_backend=ADTypes.AutoForwardDiff()) @test model.dim == 1 @test isfinite(model.logdensity([0.0])) @@ -97,7 +97,7 @@ end end @testset "DynamicPPLExt: convenience constructor uses linked space for constrained models" begin - model = DensityModel(beta_model()) + model = DensityModel(beta_model(); ad_backend=ADTypes.AutoForwardDiff()) @test model.dim == 1 @test isfinite(model.logdensity([-0.4])) @@ -105,7 +105,7 @@ end end @testset "DynamicPPLExt: generic Turing model works with ParallelMALA and default Enzyme HVP" begin - model = DensityModel(normal_model(TRUE_OBS)) + model = DensityModel(normal_model(TRUE_OBS); ad_backend=ADTypes.AutoForwardDiff()) @test model.hvp === nothing @@ -133,7 +133,7 @@ end end @testset "DynamicPPLExt: MvNormal(zeros(2), I) runs with ParallelMALA" begin - model = DensityModel(mvnormal_2d_model()) + model = DensityModel(mvnormal_2d_model(); ad_backend=ADTypes.AutoForwardDiff()) @test model.dim == 2 @test isfinite(model.logdensity(zeros(2))) @@ -163,7 +163,7 @@ end Dirichlet(ones(3)) lives on a 2-simplex, so its unconstrained representation has dim 2. Bijectors handles the link/unlink. =# - model = DensityModel(dirichlet_3_model()) + model = DensityModel(dirichlet_3_model(); ad_backend=ADTypes.AutoForwardDiff()) @test model.dim == 2 @test isfinite(model.logdensity(zeros(2))) @@ -184,24 +184,24 @@ end @test chain isa VNChain @test all(isfinite, Array(chain)) end - -@testset "DynamicPPLExt: ParallelMALA bundle_samples fallback path (thinning)" begin - #= A non-default kwarg (here `thinning`) forces ParallelMALA's `mcmcsample` override =# - model = DensityModel(mvnormal_2d_model()) - sampler = ParallelMALASampler( - 0.2; T=8, maxiter=80, tol_abs=1e-4, tol_rel=1e-3, backend=ADTypes.AutoEnzyme() - ) - chain = sample( - MersenneTwister(3), model, sampler, 800; - initial_params=zeros(2), chain_type=VNChain, thinning=2, progress=false, - ) - @test chain isa VNChain - @test only(FlexiChains.parameters(chain)) == @varname(x) - @test all(isfinite, Array(chain)) -end - + +@testset "DynamicPPLExt: ParallelMALA bundle_samples fallback path (thinning)" begin + #= A non-default kwarg (here `thinning`) forces ParallelMALA's `mcmcsample` override =# + model = DensityModel(mvnormal_2d_model(); ad_backend=ADTypes.AutoForwardDiff()) + sampler = ParallelMALASampler( + 0.2; T=8, maxiter=80, tol_abs=1e-4, tol_rel=1e-3, backend=ADTypes.AutoEnzyme() + ) + chain = sample( + MersenneTwister(3), model, sampler, 800; + initial_params=zeros(2), chain_type=VNChain, thinning=2, progress=false, + ) + @test chain isa VNChain + @test only(FlexiChains.parameters(chain)) == @varname(x) + @test all(isfinite, Array(chain)) +end + @testset "DynamicPPLExt: named columns in Chains output" begin - model = DensityModel(normal_model(TRUE_OBS)) + model = DensityModel(normal_model(TRUE_OBS); ad_backend=ADTypes.AutoForwardDiff()) chain = sample( MersenneTwister(2), @@ -219,7 +219,7 @@ end end @testset "discard_warmup=true removes warmup samples" begin - model = DensityModel(normal_model(TRUE_OBS)) + model = DensityModel(normal_model(TRUE_OBS); ad_backend=ADTypes.AutoForwardDiff()) n_warmup = 200 n_total = 800 sampler = AdaptiveMALASampler(0.3; n_warmup=n_warmup) @@ -248,7 +248,7 @@ end end @testset "posterior mean and variance match analytic solution" begin - model = DensityModel(normal_model(TRUE_OBS)) + model = DensityModel(normal_model(TRUE_OBS); ad_backend=ADTypes.AutoForwardDiff()) n_warmup = 2_000 n_draw = 10_000 sampler = AdaptiveMALASampler(0.3; n_warmup=n_warmup) @@ -271,7 +271,7 @@ end @testset "multivariate model: named columns for each dimension" begin obs = [1.0, -1.0] - model = DensityModel(mv_model(obs)) + model = DensityModel(mv_model(obs); ad_backend=ADTypes.AutoForwardDiff()) # 2 from linked Dirichlet + 2 from product_distribution @test model.dim == 4 From 42e979093c94df2416355147642f516435351aa2 Mon Sep 17 00:00:00 2001 From: Ryan Senne Date: Tue, 7 Jul 2026 13:15:07 -0400 Subject: [PATCH 4/5] Add .gitignore entry --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 367f7a7..27204ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ *.jl.*.cov *.jl.cov *.jl.mem +lcov.info +# codecov uploader binaries fetched by dev/gpu-coverage.sh +/codecov +/codecov-legacy *.rej .DS_Store .benchmarkci From 675d89e511129e3886a822d9e7e6ece6f5923a03 Mon Sep 17 00:00:00 2001 From: Ryan Senne Date: Tue, 7 Jul 2026 13:19:40 -0400 Subject: [PATCH 5/5] Edit .yml files --- .github/workflows/ReusableTest.yml | 2 ++ codecov.yml | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/ReusableTest.yml b/.github/workflows/ReusableTest.yml index 2dbac1f..b92b9cf 100644 --- a/.github/workflows/ReusableTest.yml +++ b/.github/workflows/ReusableTest.yml @@ -45,6 +45,8 @@ jobs: - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 if: ${{ inputs.run_codecov }} + with: + directories: src,ext - uses: codecov/codecov-action@v7 if: ${{ inputs.run_codecov }} with: diff --git a/codecov.yml b/codecov.yml index 86449a0..1962528 100644 --- a/codecov.yml +++ b/codecov.yml @@ -8,3 +8,11 @@ coverage: default: target: 90% range: "80...90" # Yellow within this range + +# ext/EnzymeExt.jl holds custom EnzymeRules (forward/augmented_primal/reverse). +# Enzyme runs those rule bodies through its own compiled derivative pipeline, +# which does not emit Julia's --code-coverage line counters, so they report as +# uncovered even though test-Owned-Matmul.jl exercises them and asserts correct +# numerics. +ignore: + - "ext/EnzymeExt.jl"