/
LogDensityProblems.jl
156 lines (107 loc) · 4.39 KB
/
LogDensityProblems.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""
A unified interface for log density problems, for
1. defining mappings to a log density (eg Bayesian for inference),
2. optionally obtaining a gradient using automatic differentiation,
3. defining a common interface for working with such log densities and gradients (eg MCMC).
These use cases utilize different parts of this package, make sure you read the
documentation.
"""
module LogDensityProblems
using ArgCheck: @argcheck
using DocStringExtensions: SIGNATURES, TYPEDEF
using Random: AbstractRNG, default_rng
####
#### interface for problems
####
"""
$(TYPEDEF)
A trait that means that a log density supports evaluating derivatives up to order `K`.
Typical values for `K` are `0` (just the log density) and `1` (log density and gradient).
"""
struct LogDensityOrder{K}
function LogDensityOrder{K}() where K
_K = Int(K)
@argcheck _K ≥ 0
new{_K}()
end
end
LogDensityOrder(K::Integer) = LogDensityOrder{K}()
Base.isless(::LogDensityOrder{A}, ::LogDensityOrder{B}) where {A, B} = A < B
"""
$(SIGNATURES)
Test if the type (or a value, for convenience) supports the log density interface.
When `nothing` is returned, it doesn't support this interface. When
`LogDensityOrder{K}()` is returned (typically with `K == 0`, `K = 1`,
or `K == 2`), derivatives up to order `K` are supported. *All other
return values are invalid*.
# Interface description
The following methods need to be implemented for the interface:
1. [`dimension`](@ref) returns the *dimension* of the domain,
2. [`logdensity`](@ref) evaluates the log density at a given point.
3. [`logdensity_and_gradient`](@ref) when `K ≥ 1`.
4. [`logdensity_gradient_and_hessian`](@ref) when `K ≥ 2`.
See also [`LogDensityProblems.stresstest`](@ref) for stress testing.
"""
capabilities(T::Type) = nothing
capabilities(x) = capabilities(typeof(x)) # convenience function
"""
dimension(ℓ)
Dimension of the input vectors `x` for log density `ℓ`. See [`logdensity`](@ref),
[`logdensity_and_gradient`](@ref).
!!! note
This function is *distinct* from `TransformedVariables.dimension`.
"""
function dimension end
"""
logdensity(ℓ, x)
Evaluate the log density `ℓ` at `x`, which has length compatible with its
[`dimension`](@ref).
Return a real number, which may or may not be finite (can also be `NaN`). Non-finite values
other than `-Inf` are invalid but do not error, caller should deal with these appropriately.
# Note about constants
Log densities can be shifted by *the same constant*, as long as it is consistent between
calls. For example,
```julia
logdensity(::StandardMultivariateNormal) = -0.5 * sum(abs2, x)
```
is a valid implementation for some callable `StandardMultivariateNormal` that would
implement the standard multivariate normal distribution (dimension ``k``) with pdf
```math
(2\\pi)^{-k/2} e^{-x'x/2}
```
"""
function logdensity end
"""
logdensity_and_gradient(ℓ, x)
Evaluate the log density `ℓ` and its gradient at `x`, which has length
compatible with its [`dimension`](@ref).
Return two values:
- the log density as real number, which equivalent to `logdensity(ℓ, x)`
- *if* the log density is finite, the gradient, an `::AbstractVector` of real numbers,
otherwise this value is arbitrary and should be ignored.
!!! note
Caller may assume ownership of results, ie that the gradient vector will not be
overwritten or reused for a different purpose.
The first argument (the log density) can be shifted by a constant, see the note for
[`logdensity`](@ref).
"""
function logdensity_and_gradient end
"""
logdensity_gradient_and_hessian(ℓ, x)
Evaluate the log density `ℓ`, its gradient, and Hessian at `x`, which
has length compatible with its [`dimension`](@ref).
Return three values:
- the log density as real number, which equivalent to `logdensity(ℓ, x)`
- *if* the log density is finite, the gradient, an `::AbstractVector` of real numbers,
otherwise this value is arbitrary and should be ignored.
- *if* the log density is finite, the Hessian, an `::AbstractMatrix` of real numbers,
otherwise this value is arbitrary and should be ignored.
!!! note
Caller may assume ownership of results, ie that the gradient and
Hessian will not be overwritten or reused for a different purpose.
The first argument (the log density) can be shifted by a constant, see the note for
[`logdensity`](@ref).
"""
function logdensity_gradient_and_hessian end
include("utilities.jl")
end # module