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
2 changes: 2 additions & 0 deletions .github/workflows/ReusableTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
8 changes: 8 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
10 changes: 4 additions & 6 deletions ext/DynamicPPLExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down
4 changes: 3 additions & 1 deletion test/test-DEER-Turing-Logistic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
50 changes: 25 additions & 25 deletions test/test-Turing-Integration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
Expand All @@ -97,15 +97,15 @@ 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]))
@test isfinite(model.grad_logdensity([-0.4])[1])
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

Expand Down Expand Up @@ -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)))
Expand Down Expand Up @@ -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)))
Expand All @@ -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),
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
Loading