-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathtransformedmeasure.jl
106 lines (75 loc) · 3.36 KB
/
transformedmeasure.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
# TODO: Compare with ChangesOfVariables.jl
abstract type AbstractTransformedMeasure <: AbstractMeasure end
abstract type AbstractPushforward <: AbstractTransformedMeasure end
abstract type AbstractPullback <: AbstractTransformedMeasure end
function gettransform(::AbstractTransformedMeasure) end
function params(::AbstractTransformedMeasure) end
function paramnames(::AbstractTransformedMeasure) end
function parent(::AbstractTransformedMeasure) end
export PushforwardMeasure
"""
struct PushforwardMeasure{FF,IF,MU,VC<:TransformVolCorr} <: AbstractPushforward
f :: FF
inv_f :: IF
origin :: MU
volcorr :: VC
end
"""
struct PushforwardMeasure{FF,IF,M,VC<:TransformVolCorr} <: AbstractPushforward
f::FF
inv_f::IF
origin::M
volcorr::VC
end
gettransform(ν::PushforwardMeasure) = ν.f
parent(ν::PushforwardMeasure) = ν.origin
function Pretty.tile(ν::PushforwardMeasure)
Pretty.list_layout(Pretty.tile.([ν.f, ν.inv_f, ν.origin]); prefix = :PushforwardMeasure)
end
@inline function logdensity_def(ν::PushforwardMeasure{FF,IF,M,<:WithVolCorr}, y) where {FF,IF,M}
x_orig, inv_ladj = with_logabsdet_jacobian(ν.inv_f, y)
logd_orig = logdensity_def(ν.origin, x_orig)
logd = float(logd_orig + inv_ladj)
neginf = oftype(logd, -Inf)
return ifelse(
# Zero density wins against infinite volume:
(isnan(logd) && logd_orig == -Inf && inv_ladj == +Inf) ||
# Maybe also for (logd_orig == -Inf) && isfinite(inv_ladj) ?
# Return constant -Inf to prevent problems with ForwardDiff:
(isfinite(logd_orig) && (inv_ladj == -Inf)),
neginf,
logd
)
end
@inline function logdensity_def(ν::PushforwardMeasure{FF,IF,M,<:NoVolCorr}, y) where {FF,IF,M}
x_orig = to_origin(ν, y)
return logdensity_def(ν.origin, x_orig)
end
insupport(ν::PushforwardMeasure, y) = insupport(transport_origin(ν), to_origin(ν, y))
testvalue(ν::PushforwardMeasure) = from_origin(ν, testvalue(transport_origin(ν)))
@inline function basemeasure(ν::PushforwardMeasure)
PushforwardMeasure(ν.f, ν.inv_f, basemeasure(transport_origin(ν)), NoVolCorr())
end
_pushfwd_dof(::Type{MU}, ::Type, dof) where MU = NoDOF{MU}()
_pushfwd_dof(::Type{MU}, ::Type{<:Tuple{Any,Real}}, dof) where MU = dof
# Assume that DOF are preserved if with_logabsdet_jacobian is functional:
@inline function getdof(ν::MU) where {MU<:PushforwardMeasure}
T = Core.Compiler.return_type(testvalue, Tuple{typeof(ν.origin)})
R = Core.Compiler.return_type(with_logabsdet_jacobian, Tuple{typeof(ν.f), T})
_pushfwd_dof(MU, R, getdof(ν.origin))
end
# Bypass `checked_var`, would require potentially costly transformation:
@inline checked_var(::PushforwardMeasure, x) = x
@inline transport_origin(ν::PushforwardMeasure) = ν.origin
@inline from_origin(ν::PushforwardMeasure, x) = ν.f(x)
@inline to_origin(ν::PushforwardMeasure, y) = ν.inv_f(y)
function Base.rand(rng::AbstractRNG, ::Type{T}, ν::PushforwardMeasure) where T
return from_origin(ν, rand(rng, T, transport_origin(ν)))
end
export pushfwd
"""
pushfwd(f, μ, volcorr = WithVolCorr())
Return the [pushforward measure](https://en.wikipedia.org/wiki/Pushforward_measure)
from `μ` the [measurable function](https://en.wikipedia.org/wiki/Measurable_function) `f`.
"""
pushfwd(f, μ, volcorr = WithVolCorr()) = PushforwardMeasure(f, inverse(f), μ, volcorr)