Skip to content

Commit 428ea4c

Browse files
authored
Merge pull request #89 from tpapp/tp/remove-transformvariables
Remove TransformVariables.
2 parents d3ea261 + a4fb30f commit 428ea4c

File tree

9 files changed

+93
-161
lines changed

9 files changed

+93
-161
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
fail-fast: false
1818
matrix:
1919
version:
20-
- '1.0' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
20+
- '1.6' # Replace this with the minimum Julia version that your package supports. E.g. if your package requires Julia 1.5 or higher, change this to '1.5'.
2121
- '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia.
2222
# - 'nightly' # NOTE: nightly disabled as it currently fails
2323
os:

Project.toml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
name = "LogDensityProblems"
22
uuid = "6fdf6af0-433a-55f7-b3ed-c6c6e0b8df7c"
33
authors = ["Tamas K. Papp <[email protected]>"]
4-
version = "0.12.0"
4+
version = "1.0.0"
55

66
[deps]
77
ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197"
88
DiffResults = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
99
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
1010
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1111
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
12-
TransformVariables = "84d833dd-6860-57f9-a1a7-6da5db126cff"
1312
UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
1413

1514
[compat]
@@ -18,9 +17,8 @@ BenchmarkTools = "1"
1817
DiffResults = "0.0, 1"
1918
DocStringExtensions = "0.8, 0.9"
2019
Requires = "0.5, 1"
21-
TransformVariables = "0.2, 0.3, 0.4, 0.5, 0.6"
2220
UnPack = "0.1, 1"
23-
julia = "1"
21+
julia = "1.6"
2422

2523
[extras]
2624
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ A common framework for implementing and using log densities for inference, provi
1313

1414
2. The [`ADgradient`](https://tamaspapp.eu/LogDensityProblems.jl/dev/#LogDensityProblems.ADgradient) which makes objects that support `logdensity` to calculate log density *values* calculate log density *gradients* using various automatic differentiation packages.
1515

16-
3. The wrapper [`TransformedLogDensity`](https://tamaspapp.eu/LogDensityProblems.jl/dev/#LogDensityProblems.TransformedLogDensity) using the [TransformVariables.jl](https://github.com/tpapp/TransformVariables.jl) package, allowing callables that take a set of parameters transformed from a flat vector of real numbers to support the `logdensity` interface.
16+
3. Various utility functions for debugging and testing log densities.
1717

18-
4. Various utility functions for debugging and testing log densities.
18+
**NOTE** As of version 1.0, transformed log densities have been moved to [TransformedLogDensities.jl](https://github.com/tpapp/TransformedLogDensities.jl). Existing code that uses `TransformedLogDensity` should add
19+
```
20+
using TransformedLogDensities
21+
```
22+
or equivalent.
1923

2024
See the [documentation](https://tpapp.github.io/LogDensityProblems.jl/dev) for details.

docs/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
44
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
55
Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c"
66
TransformVariables = "84d833dd-6860-57f9-a1a7-6da5db126cff"
7+
TransformedLogDensities = "f9bc47f6-f3f8-4f3b-ab21-f8bc73906f26"
78
UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
89
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
910

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Documenter, LogDensityProblems, ForwardDiff, Tracker, Zygote, BenchmarkTools
1+
using Documenter, LogDensityProblems, ForwardDiff, Tracker, Zygote, BenchmarkTools, TransformedLogDensities
22

33
makedocs(
44
sitename = "LogDensityProblems.jl",

docs/src/index.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,11 @@ problem((μ = 0.0, σ = 1.0))
8787

8888
In our example, we require ``\sigma > 0``, otherwise the problem is meaningless. However, many MCMC samplers prefer to operate on *unconstrained* spaces ``\mathbb{R}^n``. The TransformVariables package was written to transform unconstrained to constrained spaces, and help with the log Jacobian correction (more on that later). That package has detailed documentation, now we just define a transformation from a length 2 vector to a `NamedTuple` with fields `μ` (unconstrained) and `σ > 0`.
8989

90+
!!! note
91+
Since version 1.0, TransformedLogDensity has been moved to the package TransformedLogDensities.
92+
9093
```@repl 1
91-
using LogDensityProblems, TransformVariables
94+
using LogDensityProblems, TransformVariables, TransformedLogDensities
9295
ℓ = TransformedLogDensity(as((μ = asℝ, σ = asℝ₊)), problem)
9396
```
9497

@@ -101,13 +104,9 @@ LogDensityProblems.logdensity(ℓ, zeros(2))
101104
!!! note
102105
Before running time-consuming algorithms like MCMC, it is advisable to test and benchmark your log density evaluations separately. The same applies to [`LogDensityProblems.logdensity_and_gradient`](@ref).
103106

104-
```@docs
105-
TransformedLogDensity
106-
```
107-
108107
## Manual unpacking and transformation
109108

110-
If you prefer to implement the transformation yourself, you just have to define the following three methods for your problem: declare that it can evaluate log densities (but not their gradient, hence the `0` order), allow the dimension of the problem to be queried, and then finally code the density calculation with the transformation. (Note that using [`TransformedLogDensity`](@ref) takes care of all of these for you, as shown above).
109+
If you prefer to implement the transformation yourself, you just have to define the following three methods for your problem: declare that it can evaluate log densities (but not their gradient, hence the `0` order), allow the dimension of the problem to be queried, and then finally code the density calculation with the transformation. (Note that using `TransformedLogDensities.TransformedLogDensity` takes care of all of these for you, as shown above).
111110

112111
```@example 1
113112
function LogDensityProblems.capabilities(::Type{<:NormalPosterior})

src/LogDensityProblems.jl

Lines changed: 5 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ documentation.
1212
"""
1313
module LogDensityProblems
1414

15-
export TransformedLogDensity, ADgradient
15+
export ADgradient
1616

1717
using ArgCheck: @argcheck
1818
using DocStringExtensions: SIGNATURES, TYPEDEF
19-
using UnPack: @unpack
20-
import Random
19+
using Random: AbstractRNG, default_rng
2120
using Requires: @require
22-
import TransformVariables
21+
using UnPack: @unpack
2322

2423
####
2524
#### interface for problems
@@ -128,50 +127,6 @@ The first argument (the log density) can be shifted by a constant, see the note
128127
"""
129128
function logdensity_and_gradient end
130129

131-
#####
132-
##### Transformed log density (typically Bayesian inference)
133-
#####
134-
135-
"""
136-
TransformedLogDensity(transformation, log_density_function)
137-
138-
A problem in Bayesian inference. Vectors of length compatible with the dimension (obtained
139-
from `transformation`) are transformed into a general object `θ` (unrestricted type, but a
140-
named tuple is recommended for clean code), correcting for the log Jacobian determinant of
141-
the transformation.
142-
143-
`log_density_function(θ)` is expected to return *real numbers*. For zero densities or
144-
infeasible `θ`s, `-Inf` or similar should be returned, but for efficiency of inference most
145-
methods recommend using `transformation` to avoid this. It is recommended that
146-
`log_density_function` is a callable object that also encapsulates the data for the problem.
147-
148-
Use the property accessors `ℓ.transformation` and `ℓ.log_density_function` to access the
149-
arguments of `ℓ::TransformedLogDensity`, these are part of the public API.
150-
151-
# Usage note
152-
153-
This is the most convenient way to define log densities, as `capabilities`, `logdensity`,
154-
and `dimension` are automatically defined. To obtain a type that supports derivatives, use
155-
[`ADgradient`](@ref).
156-
"""
157-
struct TransformedLogDensity{T <: TransformVariables.AbstractTransform, L}
158-
transformation::T
159-
log_density_function::L
160-
end
161-
162-
function Base.show(io::IO, ℓ::TransformedLogDensity)
163-
print(io, "TransformedLogDensity of dimension $(dimension(ℓ))")
164-
end
165-
166-
capabilities(::Type{<:TransformedLogDensity}) = LogDensityOrder{0}()
167-
168-
dimension(p::TransformedLogDensity) = TransformVariables.dimension(p.transformation)
169-
170-
function logdensity(p::TransformedLogDensity, x::AbstractVector)
171-
@unpack transformation, log_density_function = p
172-
TransformVariables.transform_logdensity(transformation, log_density_function, x)
173-
end
174-
175130
#####
176131
##### AD wrappers --- interface and generic code
177132
#####
@@ -244,38 +199,8 @@ function __init__()
244199
@require Enzyme="7da242da-08ed-463a-9acd-ee780be4f1d9" include("AD_Enzyme.jl")
245200
end
246201

247-
####
248-
#### stress testing
249-
####
250-
251-
"""
252-
$(SIGNATURES)
253-
254-
Test `ℓ` with random values.
255-
256-
`N` random vectors are drawn from a standard multivariate Cauchy distribution, scaled with
257-
`scale` (which can be a scalar or a conformable vector).
258-
259-
Each random vector is then used as an argument in `f(ℓ, ...)`. `logdensity` and
260-
`logdensity_and_gradient` are recommended for `f`.
261-
262-
In case the call produces an error, the value is recorded as a failure, which are returned
263-
by the function.
202+
include("utilities.jl")
264203

265-
Not exported, but part of the API.
266-
"""
267-
function stresstest(f, ℓ; N = 1000, rng::Random.AbstractRNG = Random.GLOBAL_RNG, scale = 1)
268-
failures = Vector{Float64}[]
269-
d = dimension(ℓ)
270-
for _ in 1:N
271-
x = TransformVariables.random_reals(d; scale = scale, cauchy = true, rng = rng)
272-
try
273-
f(ℓ, x)
274-
catch e
275-
push!(failures, x)
276-
end
277-
end
278-
failures
279-
end
204+
Base.@deprecate_moved TransformedLogDensity "TransformedLogDensities"
280205

281206
end # module

src/utilities.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#####
2+
##### utilities
3+
#####
4+
5+
####
6+
#### random reals
7+
####
8+
9+
function _random_reals_scale(rng::AbstractRNG, scale::Real, cauchy::Bool)
10+
cauchy ? scale / abs2(randn(rng)) : scale * 1.0
11+
end
12+
13+
"""
14+
$(SIGNATURES)
15+
16+
Random vector in ``ℝⁿ`` of length `n`.
17+
18+
A standard multivaritate normal or Cauchy is used, depending on `cauchy`, then scaled with
19+
`scale`. `rng` is the random number generator used.
20+
21+
Not exported, but part of the API.
22+
"""
23+
function random_reals(n::Integer; scale::Real = 1, cauchy::Bool = false,
24+
rng::AbstractRNG = default_rng())
25+
randn(rng, n) .* _random_reals_scale(rng, scale, cauchy)
26+
end
27+
28+
####
29+
#### stress testing
30+
####
31+
32+
"""
33+
$(SIGNATURES)
34+
35+
Test `ℓ` with random values.
36+
37+
`N` random vectors are drawn from a standard multivariate Cauchy distribution, scaled with
38+
`scale` (which can be a scalar or a conformable vector).
39+
40+
Each random vector is then used as an argument in `f(ℓ, ...)`. `logdensity` and
41+
`logdensity_and_gradient` are recommended for `f`.
42+
43+
In case the call produces an error, the value is recorded as a failure, which are returned
44+
by the function.
45+
46+
Not exported, but part of the API.
47+
"""
48+
function stresstest(f, ℓ; N = 1000, rng::AbstractRNG = default_rng(), scale = 1)
49+
failures = Vector{Float64}[]
50+
d = dimension(ℓ)
51+
for _ in 1:N
52+
x = random_reals(d; scale = scale, cauchy = true, rng = rng)
53+
try
54+
f(ℓ, x)
55+
catch e
56+
push!(failures, x)
57+
end
58+
end
59+
failures
60+
end

test/runtests.jl

Lines changed: 12 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
struct EnzymeTestMode <: Enzyme.Mode end
1010
end
1111

12-
using LogDensityProblems, Test, Distributions, TransformVariables, BenchmarkTools
12+
using LogDensityProblems, Test, Distributions, BenchmarkTools
1313
import LogDensityProblems: capabilities, dimension, logdensity
1414
using LogDensityProblems: logdensity_and_gradient, LogDensityOrder
1515

16-
import ForwardDiff, Tracker, TransformVariables, Random, Zygote, ReverseDiff
16+
import ForwardDiff, Tracker, Random, Zygote, ReverseDiff
1717
using UnPack: @unpack
1818

1919
####
@@ -65,7 +65,7 @@ end
6565
end
6666

6767
###
68-
### simple log density for testing
68+
### simple log densities for testing
6969
###
7070

7171
struct TestLogDensity{F}
@@ -79,6 +79,10 @@ test_gradient(x) = x .* [-4, -6, -10]
7979
TestLogDensity() = TestLogDensity(test_logdensity) # default: -Inf for negative input
8080
Base.show(io::IO, ::TestLogDensity) = print(io, "TestLogDensity")
8181

82+
struct TestLogDensity2 end
83+
logdensity(::TestLogDensity2, x) = -sum(abs2, x)
84+
dimension(::TestLogDensity2) = 20
85+
8286
####
8387
#### traits
8488
####
@@ -153,13 +157,6 @@ end
153157
@test LogDensityProblems.heuristic_chunks(82) == vcat(1:4:81, [82])
154158
end
155159

156-
@testset "benchmark ForwardDiff chunk size" begin
157-
= TransformedLogDensity(as(Array, 20), x -> -sum(abs2, x))
158-
b = LogDensityProblems.benchmark_ForwardDiff_chunks(ℓ)
159-
@test b isa Vector{Pair{Int,Float64}}
160-
@test length(b) 20
161-
end
162-
163160
@testset "AD via Tracker" begin
164161
= TestLogDensity()
165162
∇ℓ = ADgradient(:Tracker, ℓ)
@@ -221,65 +218,13 @@ end
221218

222219
@testset "ADgradient missing method" begin
223220
msg = "Don't know how to AD with Foo, consider `import Foo` if there is such a package."
224-
P = TransformedLogDensity(as(Array, 1), x -> sum(abs2, x))
225-
@test_logs((:info, msg), @test_throws(MethodError, ADgradient(:Foo, P)))
226-
end
227-
228-
####
229-
#### transformed Bayesian problem
230-
####
231-
232-
@testset "transformed Bayesian problem" begin
233-
t = as((y = asℝ₊, ))
234-
d = LogNormal(1.0, 2.0)
235-
logposterior = ((x, ), ) -> logpdf(d, x)
236-
237-
# a Bayesian problem
238-
p = TransformedLogDensity(t, logposterior)
239-
@test repr(p) == "TransformedLogDensity of dimension 1"
240-
@test dimension(p) == 1
241-
@test p.transformation t
242-
@test capabilities(p) == LogDensityOrder(0)
243-
244-
# gradient of a problem
245-
∇p = ADgradient(:ForwardDiff, p)
246-
@test dimension(∇p) == 1
247-
@test parent(∇p).transformation t
248-
249-
for _ in 1:100
250-
x = random_arg(t)
251-
θ, lj = transform_and_logjac(t, x)
252-
px = logdensity(p, x)
253-
@test logpdf(d, θ.y) + lj (px::Real)
254-
px2, ∇px = logdensity_and_gradient(∇p, x)
255-
@test px2 == px
256-
@test ∇px [ForwardDiff.derivative(x -> logpdf(d, exp(x)) + x, x[1])]
257-
end
221+
@test_logs((:info, msg), @test_throws(MethodError, ADgradient(:Foo, TestLogDensity2())))
258222
end
259223

260-
@testset "-∞ log densities" begin
261-
t = as(Array, 2)
262-
validx = x -> all(x .> 0)
263-
p = TransformedLogDensity(t, x -> validx(x) ? sum(abs2, x)/2 : -Inf)
264-
∇p = ADgradient(:ForwardDiff, p)
265-
266-
@test dimension(p) == dimension(∇p) == TransformVariables.dimension(t)
267-
@test p.transformation parent(∇p).transformation t
268-
269-
for _ in 1:100
270-
x = random_arg(t)
271-
px = logdensity(∇p, x)
272-
px_∇px = logdensity_and_gradient(∇p, x)
273-
@test px isa Real
274-
@test px_∇px isa Tuple{Real,Any}
275-
@test first(px_∇px) px
276-
if validx(x)
277-
@test px sum(abs2, x)/2
278-
@test last(px_∇px) x
279-
else
280-
@test isinf(px)
281-
end
282-
end
224+
@testset "benchmark ForwardDiff chunk size" begin
225+
b = LogDensityProblems.benchmark_ForwardDiff_chunks(TestLogDensity2())
226+
@test b isa Vector{Pair{Int,Float64}}
227+
@test length(b) 20
283228
end
284229

285230
@testset "stresstest" begin

0 commit comments

Comments
 (0)