Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
502949d
Generalise Hamiltonian derivative calls for RHMC case
THargreaves Oct 16, 2025
312b2d9
Update riemannian metric to remove unnecessary matrix inversion
J-Price-3 Nov 6, 2025
bd583e9
Fix bug that prevents eltype working for DenseRiemannianMetric
J-Price-3 Nov 10, 2025
2020f2d
Divergence protection for generalized leapfrog integrator.
J-Price-3 Nov 10, 2025
4fe83b9
Revert "Divergence protection for generalized leapfrog integrator."
J-Price-3 Nov 13, 2025
dfcbb53
Change eltype for DenseRiemannianMetric
J-Price-3 Nov 13, 2025
ce20e4b
Optimise riemannian hmc utility functions.
J-Price-3 Nov 20, 2025
a5373d9
Fix type stability for jacobian of hessian.
J-Price-3 Nov 28, 2025
d96c29c
Fix DenseRiemannianMetric type instability.
J-Price-3 Nov 28, 2025
7d4a86f
Small optimisations for hamiltonian.jl
J-Price-3 Nov 28, 2025
8f1ebc5
Use hessian symmetry for jacobian of hessian
J-Price-3 Nov 29, 2025
981932e
Implement Implicit Midpoint integrator
J-Price-3 Nov 29, 2025
9cac513
Merge remote-tracking branch 'origin/qqy/NEW_RMHMC' into Jamie_RHMC
J-Price-3 Dec 2, 2025
061e096
Merge remote-tracking branch 'origin/qqy/NEW_RMHMC' into Jamie_RHMC
J-Price-3 Dec 2, 2025
9688206
Merge branch 'Jamie_RHMC' of https://github.com/TuringLang/AdvancedHM…
J-Price-3 Dec 4, 2025
d96d939
Remove riemannian utils from build
J-Price-3 Dec 4, 2025
ca4d2bd
Remove unbound type parameter from DenseRiemannianMetric
J-Price-3 Dec 4, 2025
346a129
Fix method overwriting
J-Price-3 Dec 4, 2025
ae057b5
Remove duplicate include
J-Price-3 Dec 4, 2025
703ed09
Remove duplicate include
J-Price-3 Dec 4, 2025
36bf35d
Add missing import
J-Price-3 Dec 4, 2025
187aa58
Fixes for tests
J-Price-3 Dec 4, 2025
4dbe9fe
Test fixes
J-Price-3 Dec 4, 2025
3de6002
Fixes for tests
J-Price-3 Dec 4, 2025
069c6a8
Fixes for tests
J-Price-3 Dec 4, 2025
b61ea01
Fixes for tests
J-Price-3 Dec 4, 2025
4d847e2
Efficient rand_momentum for softabs
J-Price-3 Dec 4, 2025
473cc93
Implements a simple Nutpie style adaptation (using both positions and…
nsiccha Dec 1, 2025
c7090ba
fix JET tests (#479)
nsiccha Nov 27, 2025
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
7 changes: 7 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# AdvancedHMC Changelog

## 0.8.4

- Introduces an experimental way to improve the *diagonal* mass matrix adaptation using gradient information (similar to [nutpie](https://github.com/pymc-devs/nutpie)),
currently to be initialized for a `metric` of type `DiagEuclideanMetric`
via `mma = AdvancedHMC.NutpieVar(size(metric); var=copy(metric.M⁻¹))`
until a new interface is introduced in an upcoming breaking release to specify the method of adaptation.
Comment on lines +6 to +8
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
currently to be initialized for a `metric` of type `DiagEuclideanMetric`
via `mma = AdvancedHMC.NutpieVar(size(metric); var=copy(metric.M⁻¹))`
until a new interface is introduced in an upcoming breaking release to specify the method of adaptation.
currently to be initialized for a `metric` of type `DiagEuclideanMetric`
via `mma = AdvancedHMC.NutpieVar(size(metric); var=copy(metric.M⁻¹))`
until a new interface is introduced in an upcoming breaking release to specify the method of adaptation.


## 0.8.0

- To make an MCMC transtion from phasepoint `z` using trajectory `τ`(or HMCKernel `κ`) under Hamiltonian `h`, use `transition(h, τ, z)` or `transition(rng, h, τ, z)`(if using HMCKernel, use `transition(h, κ, z)` or `transition(rng, h, κ, z)`).
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "AdvancedHMC"
uuid = "0bf59076-c3b1-5ca4-86bd-e02cd72cde3d"
version = "0.8.3"
version = "0.8.4"

[deps]
AbstractMCMC = "80f14c24-f653-4e6a-9b94-39d6b0f70001"
Expand Down
12 changes: 8 additions & 4 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ where `ϵ` is the step size of leapfrog integration.
### Adaptor (`adaptor`)

- Adapt the mass matrix `metric` of the Hamiltonian dynamics: `mma = MassMatrixAdaptor(metric)`

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change

+ This is lowered to `UnitMassMatrix`, `WelfordVar` or `WelfordCov` based on the type of the mass matrix `metric`
+ There is an experimental way to improve the *diagonal* mass matrix adaptation using gradient information (similar to [nutpie](https://github.com/pymc-devs/nutpie)),
currently to be initialized for a `metric` of type `DiagEuclideanMetric`
via `mma = AdvancedHMC.NutpieVar(size(metric); var=copy(metric.M⁻¹))`
until a new interface is introduced in an upcoming breaking release to specify the method of adaptation.
Comment on lines +38 to +40
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
currently to be initialized for a `metric` of type `DiagEuclideanMetric`
via `mma = AdvancedHMC.NutpieVar(size(metric); var=copy(metric.M⁻¹))`
until a new interface is introduced in an upcoming breaking release to specify the method of adaptation.
currently to be initialized for a `metric` of type `DiagEuclideanMetric`
via `mma = AdvancedHMC.NutpieVar(size(metric); var=copy(metric.M⁻¹))`
until a new interface is introduced in an upcoming breaking release to specify the method of adaptation.


- Adapt the step size of the leapfrog integrator `integrator`: `ssa = StepSizeAdaptor(δ, integrator)`

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change

+ It uses Nesterov's dual averaging with `δ` as the target acceptance rate.
- Combine the two above *naively*: `NaiveHMCAdaptor(mma, ssa)`
- Combine the first two using Stan's windowed adaptation: `StanHMCAdaptor(mma, ssa)`
Expand All @@ -61,12 +65,12 @@ sample(
Draw `n_samples` samples using the kernel `κ` under the Hamiltonian system `h`

- The randomness is controlled by `rng`.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change

+ If `rng` is not provided, the default random number generator (`Random.default_rng()`) will be used.

- The initial point is given by `θ`.
- The adaptor is set by `adaptor`, for which the default is no adaptation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change

+ It will perform `n_adapts` steps of adaptation, for which the default is `1_000` or 10% of `n_samples`, whichever is lower.
- `drop_warmup` specifies whether to drop samples.
- `verbose` controls the verbosity.
Expand Down
61 changes: 44 additions & 17 deletions research/src/riemannian_hmc_utility.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,74 @@ using Random, LinearAlgebra, ReverseDiff, ForwardDiff, MCMCLogDensityProblems

# Fisher information metric
function gen_∂G∂θ_rev(Vfunc, x; f=identity)
_Hfunc = MCMCLogDensityProblems.gen_hess(Vfunc, ReverseDiff.track.(x))
Hfunc = x -> _Hfunc(x)[3]
Hfunc = gen_hess_fwd(Vfunc, ReverseDiff.track.(x))

# QUES What's the best output format of this function?
return x -> ReverseDiff.jacobian(x -> f(Hfunc(x)), x) # default output shape [∂H∂x₁; ∂H∂x₂; ...]
end

# TODO Refactor this using https://juliadiff.org/ForwardDiff.jl/stable/user/api/#Preallocating/Configuring-Work-Buffers
function gen_hess_fwd_precompute_cfg(func, x::AbstractVector)
cfg = ForwardDiff.HessianConfig(func, x)
H = Matrix{eltype(x)}(undef, length(x), length(x))

function hess(x::AbstractVector)
ForwardDiff.hessian!(H, func, x, cfg)
return H
end
return hess
end

function gen_hess_fwd(func, x::AbstractVector)
cfg = nothing
H = nothing

function hess(x::AbstractVector)
return nothing, nothing, ForwardDiff.hessian(func, x)
if cfg === nothing
cfg = ForwardDiff.HessianConfig(func, x)
H = Matrix{eltype(x)}(undef, length(x), length(x))
end
ForwardDiff.hessian!(H, func, x, cfg)
return H
end
return hess
end

function gen_∂G∂θ_fwd(Vfunc, x; f=identity)
_Hfunc = gen_hess_fwd(Vfunc, x)
Hfunc = x -> _Hfunc(x)[3]
# QUES What's the best output format of this function?
cfg = ForwardDiff.JacobianConfig(Hfunc, x)
chunk = ForwardDiff.Chunk(x)
tag = ForwardDiff.Tag(Vfunc, eltype(x))
jac_cfg = ForwardDiff.JacobianConfig(Vfunc, x, chunk, tag)
hess_cfg = ForwardDiff.HessianConfig(Vfunc, jac_cfg.duals, chunk, tag)

d = length(x)
out = zeros(eltype(x), d^2, d)
return x -> ForwardDiff.jacobian!(out, Hfunc, x, cfg)
return out # default output shape [∂H∂x₁; ∂H∂x₂; ...]

function ∂G∂θ_fwd(y)
hess = z -> Symmetric(ForwardDiff.hessian(Vfunc, z, hess_cfg, Val{false}()))
ForwardDiff.jacobian!(out, hess, y, jac_cfg, Val{false}())
return out
end

return ∂G∂θ_fwd
end
# 1.764 ms
# fwd -> 5.338 μs
# cfg -> 3.651 μs

function reshape_∂G∂θ(H)
d = size(H, 2)
return cat((H[((i - 1) * d + 1):(i * d), :] for i in 1:d)...; dims=3)
return reshape(H, d, d, :)
end

function prepare_sample_target(hps, θ₀, ℓπ)
Vfunc = x -> -ℓπ(x) # potential energy is the negative log-probability
_Hfunc = MCMCLogDensityProblems.gen_hess(Vfunc, θ₀) # x -> (value, gradient, hessian)
Hfunc = x -> copy.(_Hfunc(x)) # _Hfunc do in-place computation, copy to avoid bug
Hfunc = gen_hess_fwd_precompute_cfg(Vfunc, θ₀) # x -> (value, gradient, hessian)

fstabilize = H -> H + hps.λ * I
fstabilize = H -> begin
@inbounds for i in 1:size(H, 1)
H[i, i] += hps.λ
end
H
end
Gfunc = x -> begin
H = fstabilize(Hfunc(x)[3])
H = fstabilize(Hfunc(x))
all(isfinite, H) ? H : diagm(ones(length(x)))
end
_∂G∂θfunc = gen_∂G∂θ_fwd(Vfunc, θ₀; f=fstabilize) # size==(4, 2)
Expand Down
10 changes: 5 additions & 5 deletions src/AdvancedHMC.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,11 @@ export Hamiltonian

include("integrator.jl")
export Leapfrog, JitteredLeapfrog, TemperedLeapfrog
include("riemannian/integrator.jl")
export GeneralizedLeapfrog

include("riemannian/metric.jl")
export IdentityMap, SoftAbsMap, DenseRiemannianMetric

export AbstractRiemannianMetric, DenseRiemannianMetric, IdentityMap, SoftAbsMap
include("riemannian/integrator.jl")
export GeneralizedLeapfrog, ImplicitMidpoint
include("riemannian/hamiltonian.jl")

include("trajectory.jl")
Expand All @@ -89,7 +88,7 @@ export find_good_eps
include("adaptation/Adaptation.jl")
using .Adaptation
import .Adaptation:
StepSizeAdaptor, MassMatrixAdaptor, StanHMCAdaptor, NesterovDualAveraging, NoAdaptation
StepSizeAdaptor, MassMatrixAdaptor, StanHMCAdaptor, NesterovDualAveraging, NoAdaptation, PositionOrPhasePoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
StepSizeAdaptor, MassMatrixAdaptor, StanHMCAdaptor, NesterovDualAveraging, NoAdaptation, PositionOrPhasePoint
StepSizeAdaptor,
MassMatrixAdaptor,
StanHMCAdaptor,
NesterovDualAveraging,
NoAdaptation,
PositionOrPhasePoint


# Helpers for initializing adaptors via AHMC structs

Expand Down Expand Up @@ -131,6 +130,7 @@ export StepSizeAdaptor,
MassMatrixAdaptor,
UnitMassMatrix,
WelfordVar,
NutpieVar,
WelfordCov,
NaiveHMCAdaptor,
StanHMCAdaptor,
Expand Down
2 changes: 1 addition & 1 deletion src/abstractmcmc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ function AbstractMCMC.step(

# Adapt h and spl.
tstat = stat(t)
h, κ, isadapted = adapt!(h, κ, adaptor, i, n_adapts, t.z, tstat.acceptance_rate)
h, κ, isadapted = adapt!(h, κ, adaptor, i, n_adapts, t.z, tstat.acceptance_rate)
tstat = merge(tstat, (is_adapt=isadapted,))

# Compute next transition and state.
Expand Down
23 changes: 12 additions & 11 deletions src/adaptation/Adaptation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ export Adaptation
using LinearAlgebra: LinearAlgebra
using Statistics: Statistics

using ..AdvancedHMC: AbstractScalarOrVec
using ..AdvancedHMC: AbstractScalarOrVec, PhasePoint
using DocStringExtensions

"""
$(TYPEDEF)

Abstract type for HMC adaptors.
Abstract type for HMC adaptors.
"""
abstract type AbstractAdaptor end
function getM⁻¹ end
Expand All @@ -21,12 +21,17 @@ function initialize! end
function finalize! end
export AbstractAdaptor, adapt!, initialize!, finalize!, reset!, getϵ, getM⁻¹

get_position(x::PhasePoint) = x.θ
get_position(x::AbstractVecOrMat{<:AbstractFloat}) = x
const PositionOrPhasePoint = Union{AbstractVecOrMat{<:AbstractFloat}, PhasePoint}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
const PositionOrPhasePoint = Union{AbstractVecOrMat{<:AbstractFloat}, PhasePoint}
const PositionOrPhasePoint = Union{AbstractVecOrMat{<:AbstractFloat},PhasePoint}


struct NoAdaptation <: AbstractAdaptor end
export NoAdaptation
include("stepsize.jl")
export StepSizeAdaptor, NesterovDualAveraging

include("massmatrix.jl")
export MassMatrixAdaptor, UnitMassMatrix, WelfordVar, WelfordCov
export MassMatrixAdaptor, UnitMassMatrix, WelfordVar, NutpieVar, WelfordCov

##
## Composite adaptors
Expand All @@ -47,18 +52,14 @@ getϵ(ca::NaiveHMCAdaptor) = getϵ(ca.ssa)
# TODO: implement consensus adaptor
function adapt!(
nca::NaiveHMCAdaptor,
θ::AbstractVecOrMat{<:AbstractFloat},
z_or_theta::PositionOrPhasePoint,
α::AbstractScalarOrVec{<:AbstractFloat},
)
adapt!(nca.ssa, θ, α)
adapt!(nca.pc, θ, α)
return nothing
end
function reset!(aca::NaiveHMCAdaptor)
reset!(aca.ssa)
reset!(aca.pc)
adapt!(nca.ssa, z_or_theta, α)
adapt!(nca.pc, z_or_theta, α)
return nothing
end

initialize!(adaptor::NaiveHMCAdaptor, n_adapts::Int) = nothing
finalize!(aca::NaiveHMCAdaptor) = finalize!(aca.ssa)

Expand Down
Loading
Loading