Skip to content

Commit d211822

Browse files
authored
Dev (#233)
* updates * DistributionMeasures * bumps dependencies * switch to StaticArraysCore * add a compat * ::Normal << LebesgueMeasure() * Lebesgue => LebesgueMeasure * logdensity_def(p::Normal, q::Normal, x) * type parameters * Affine => AffinePushfwd * mean and std of AffinePushfwd * bump version * updates to `Normal` * format * Update docs/src/affine.md * Update docs/src/affine.md * bump version
1 parent 5311dff commit d211822

22 files changed

+168
-120
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MeasureTheory"
22
uuid = "eadaa1a4-d27c-401d-8699-e962e1bbc33b"
33
authors = ["Chad Scherrer <[email protected]> and contributors"]
4-
version = "0.17.3"
4+
version = "0.18.0"
55

66
[deps]
77
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
@@ -70,7 +70,7 @@ Static = "0.5, 0.6"
7070
StaticArraysCore = "1"
7171
StatsBase = "0.32, 0.33"
7272
StatsFuns = "0.9, 1"
73-
TransformVariables = "0.5, 0.6"
73+
TransformVariables = "0.5, 0.6, 0.7"
7474
Tricks = "0.1"
7575
julia = "1.6"
7676

docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pages = [
88
"Home" => "index.md",
99
"Tutorials" => [
1010
"Adding a new measure" => "adding.md",
11-
"Affine transformations" => "affine.md",
11+
"AffinePushfwd transformations" => "affine.md",
1212
],
1313
"API Reference" => [
1414
"MeasureBase" => "api_measurebase.md",

docs/src/affine.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,14 @@ julia> f⁻¹(f(4))
104104
4.0
105105
```
106106

107-
## `Affine`
107+
## `AffinePushfwd`
108108

109-
Of particular interest (the whole point of all of this, really) is to have a natural way to work with affine transformations of measures. In accordance with the principle of "common things should have shorter names", we call this `Affine`.
109+
Of particular interest (the whole point of all of this, really) is to have a natural way to work with affine transformations of measures. In accordance with the principle of "common things should have shorter names", we call this `AffinePushfwd`.
110110

111-
The structure of `Affine` is relatively simple:
111+
The structure of `AffinePushfwd` is relatively simple:
112112

113113
```julia
114-
struct Affine{N,M,T} <: AbstractMeasure
114+
struct AffinePushfwd{N,M,T} <: AbstractMeasure
115115
f::AffineTransform{N,T}
116116
parent::M
117117
end

src/MeasureTheory.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ using Tricks: static_hasmethod
8888
using Static
8989

9090
export as
91-
export Affine
91+
export AffinePushfwd
9292
export AffineTransform
9393
export insupport
9494
export For

src/combinators/affine.jl

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export Affine, AffineTransform
1+
export AffinePushfwd, AffineTransform
22
using LinearAlgebra
33
import Base
44

@@ -161,42 +161,42 @@ basemeasure(d::OrthoLebesgue) = d
161161

162162
logdensity_def(::OrthoLebesgue, x) = static(0.0)
163163

164-
struct Affine{N,M,T} <: AbstractMeasure
164+
struct AffinePushfwd{N,M,T} <: AbstractMeasure
165165
f::AffineTransform{N,T}
166166
parent::M
167167
end
168168

169-
function Pretty.tile(d::Affine)
169+
function Pretty.tile(d::AffinePushfwd)
170170
pars = Pretty.literal(sprint(show, params(d.f); context = :compact => true))
171171

172-
Pretty.list_layout([pars, Pretty.tile(d.parent)]; prefix = :Affine)
172+
Pretty.list_layout([pars, Pretty.tile(d.parent)]; prefix = :AffinePushfwd)
173173
end
174174

175-
@inline function testvalue(d::Affine)
175+
@inline function testvalue(d::AffinePushfwd)
176176
f = getfield(d, :f)
177177
z = testvalue(parent(d))
178178
return f(z)
179179
end
180180

181-
Affine(nt::NamedTuple, μ::AbstractMeasure) = affine(nt, μ)
181+
AffinePushfwd(nt::NamedTuple, μ::AbstractMeasure) = affine(nt, μ)
182182

183-
Affine(nt::NamedTuple) = affine(nt)
183+
AffinePushfwd(nt::NamedTuple) = affine(nt)
184184

185-
Base.parent(d::Affine) = getfield(d, :parent)
185+
Base.parent(d::AffinePushfwd) = getfield(d, :parent)
186186

187-
function params::Affine)
187+
function params::AffinePushfwd)
188188
nt1 = getfield(getfield(μ, :f), :par)
189189
nt2 = params(parent(μ))
190190
return merge(nt1, nt2)
191191
end
192192

193-
function paramnames(::Type{A}) where {N,M,A<:Affine{N,M}}
193+
function paramnames(::Type{A}) where {N,M,A<:AffinePushfwd{N,M}}
194194
tuple(union(N, paramnames(M))...)
195195
end
196196

197-
Base.propertynames(d::Affine{N}) where {N} = N (:parent, :f)
197+
Base.propertynames(d::AffinePushfwd{N}) where {N} = N (:parent, :f)
198198

199-
@inline function Base.getproperty(d::Affine, s::Symbol)
199+
@inline function Base.getproperty(d::AffinePushfwd, s::Symbol)
200200
if s === :parent
201201
return getfield(d, :parent)
202202
elseif s === :f
@@ -206,42 +206,42 @@ Base.propertynames(d::Affine{N}) where {N} = N ∪ (:parent, :f)
206206
end
207207
end
208208

209-
Base.size(d::Affine) = size(d.μ)
210-
Base.size(d::Affine{(:σ,)}) = (size(d.σ, 1),)
211-
Base.size(d::Affine{(:λ,)}) = (size(d.λ, 2),)
209+
Base.size(d::AffinePushfwd) = size(d.μ)
210+
Base.size(d::AffinePushfwd{(:σ,)}) = (size(d.σ, 1),)
211+
Base.size(d::AffinePushfwd{(:λ,)}) = (size(d.λ, 2),)
212212

213-
@inline function logdensity_def(d::Affine{(:σ,)}, x::AbstractArray)
213+
@inline function logdensity_def(d::AffinePushfwd{(:σ,)}, x::AbstractArray)
214214
z = solve(d.σ, x)
215215
MeasureBase.unsafe_logdensityof(d.parent, z)
216216
end
217217

218-
@inline function logdensity_def(d::Affine{(:λ,)}, x::AbstractArray)
218+
@inline function logdensity_def(d::AffinePushfwd{(:λ,)}, x::AbstractArray)
219219
z = d.λ * x
220220
MeasureBase.unsafe_logdensityof(d.parent, z)
221221
end
222222

223-
@inline function logdensity_def(d::Affine{(:μ,)}, x::AbstractArray)
223+
@inline function logdensity_def(d::AffinePushfwd{(:μ,)}, x::AbstractArray)
224224
z = mappedarray(-, x, d.μ)
225225
MeasureBase.unsafe_logdensityof(d.parent, z)
226226
end
227227

228-
@inline function logdensity_def(d::Affine{(:μ, :σ)}, x::AbstractArray)
228+
@inline function logdensity_def(d::AffinePushfwd{(:μ, :σ)}, x::AbstractArray)
229229
z = d.σ \ mappedarray(-, x, d.μ)
230230
MeasureBase.unsafe_logdensityof(d.parent, z)
231231
end
232232

233-
@inline function logdensity_def(d::Affine{(:μ, :λ)}, x::AbstractArray)
233+
@inline function logdensity_def(d::AffinePushfwd{(:μ, :λ)}, x::AbstractArray)
234234
z = d.λ * mappedarray(-, x, d.μ)
235235
MeasureBase.unsafe_logdensityof(d.parent, z)
236236
end
237237

238-
@inline function logdensity_def(d::Affine, x)
238+
@inline function logdensity_def(d::AffinePushfwd, x)
239239
z = inverse(d.f)(x)
240240
logdensity_def(d.parent, z)
241241
end
242242

243-
# # # logdensity_def(d::Affine{(:μ,:λ)}, x) = logdensity_def(d.parent, d.σ \ (x - d.μ))
244-
# # @inline function logdensity_def(d::Affine{(:μ,:σ), P, Tuple{V,M}}, x) where {P, V<:AbstractVector, M<:AbstractMatrix}
243+
# # # logdensity_def(d::AffinePushfwd{(:μ,:λ)}, x) = logdensity_def(d.parent, d.σ \ (x - d.μ))
244+
# # @inline function logdensity_def(d::AffinePushfwd{(:μ,:σ), P, Tuple{V,M}}, x) where {P, V<:AbstractVector, M<:AbstractMatrix}
245245
# # z = x - d.μ
246246
# # σ = d.σ
247247
# # if σ isa Factorization
@@ -252,45 +252,45 @@ end
252252
# # sum(zⱼ -> logdensity_def(d.parent, zⱼ), z)
253253
# # end
254254

255-
# # # logdensity_def(d::Affine{(:μ,:λ)}, x) = logdensity_def(d.parent, d.λ * (x - d.μ))
256-
# # @inline function logdensity_def(d::Affine{(:μ,:λ), P,Tuple{V,M}}, x) where {P,V<:AbstractVector, M<:AbstractMatrix}
255+
# # # logdensity_def(d::AffinePushfwd{(:μ,:λ)}, x) = logdensity_def(d.parent, d.λ * (x - d.μ))
256+
# # @inline function logdensity_def(d::AffinePushfwd{(:μ,:λ), P,Tuple{V,M}}, x) where {P,V<:AbstractVector, M<:AbstractMatrix}
257257
# # z = x - d.μ
258258
# # lmul!(d.λ, z)
259259
# # logdensity_def(d.parent, z)
260260
# # end
261261

262-
@inline function basemeasure(d::Affine{N,M,Tuple{A}}) where {N,M,A<:AbstractArray}
262+
@inline function basemeasure(d::AffinePushfwd{N,M,Tuple{A}}) where {N,M,A<:AbstractArray}
263263
weightedmeasure(-logjac(d), OrthoLebesgue(params(d)))
264264
end
265265

266266
@inline function basemeasure(
267-
d::MeasureTheory.Affine{N,L,Tuple{A}},
267+
d::MeasureTheory.AffinePushfwd{N,L,Tuple{A}},
268268
) where {N,L<:MeasureBase.Lebesgue,A<:AbstractArray}
269269
weightedmeasure(-logjac(d), OrthoLebesgue(params(d)))
270270
end
271271

272272
@inline function basemeasure(
273-
d::Affine{N,M,Tuple{A1,A2}},
273+
d::AffinePushfwd{N,M,Tuple{A1,A2}},
274274
) where {N,M,A1<:AbstractArray,A2<:AbstractArray}
275275
weightedmeasure(-logjac(d), OrthoLebesgue(params(d)))
276276
end
277277

278-
@inline basemeasure(d::Affine) = affine(getfield(d, :f), basemeasure(d.parent))
278+
@inline basemeasure(d::AffinePushfwd) = affine(getfield(d, :f), basemeasure(d.parent))
279279

280280
# We can't do this until we know we're working with Lebesgue measure, since for
281281
# example it wouldn't make sense to apply a log-Jacobian to a point measure
282-
@inline function basemeasure(d::Affine{N,L}) where {N,L<:Lebesgue}
282+
@inline function basemeasure(d::AffinePushfwd{N,L}) where {N,L<:Lebesgue}
283283
weightedmeasure(-logjac(d), d.parent)
284284
end
285-
@inline function basemeasure(d::Affine{N,L}) where {N,L<:LebesgueMeasure}
285+
@inline function basemeasure(d::AffinePushfwd{N,L}) where {N,L<:LebesgueMeasure}
286286
weightedmeasure(-logjac(d), d.parent)
287287
end
288288

289-
logjac(d::Affine) = logjac(getfield(d, :f))
289+
logjac(d::AffinePushfwd) = logjac(getfield(d, :f))
290290

291291
function Random.rand!(
292292
rng::Random.AbstractRNG,
293-
d::Affine,
293+
d::AffinePushfwd,
294294
x::AbstractVector{T},
295295
z = Vector{T}(undef, size(getfield(d, :f), 2)),
296296
) where {T}
@@ -300,42 +300,67 @@ function Random.rand!(
300300
return x
301301
end
302302

303-
# function Base.rand(rng::Random.AbstractRNG, ::Type{T}, d::Affine) where {T}
303+
# function Base.rand(rng::Random.AbstractRNG, ::Type{T}, d::AffinePushfwd) where {T}
304304
# f = getfield(d, :f)
305305
# z = rand(rng, T, parent(d))
306306
# apply!(x, f, z)
307307
# return z
308308
# end
309309

310-
supportdim(nt::NamedTuple{(:μ, :σ)}) = colsize(nt.σ)
311-
supportdim(nt::NamedTuple{(:μ, :λ)}) = rowsize(nt.λ)
312-
supportdim(nt::NamedTuple{(:σ,)}) = colsize(nt.σ)
313-
supportdim(nt::NamedTuple{(:λ,)}) = rowsize(nt.λ)
314-
supportdim(nt::NamedTuple{(:μ,)}) = size(nt.μ)
310+
supportdim(nt::NamedTuple{(:μ, :σ),T}) where {T} = colsize(nt.σ)
311+
supportdim(nt::NamedTuple{(:μ, :λ),T}) where {T} = rowsize(nt.λ)
312+
supportdim(nt::NamedTuple{(:σ,),T}) where {T} = colsize(nt.σ)
313+
supportdim(nt::NamedTuple{(:λ,),T}) where {T} = rowsize(nt.λ)
314+
supportdim(nt::NamedTuple{(:μ,),T}) where {T} = size(nt.μ)
315315

316-
asparams(::Affine, ::StaticSymbol{:μ}) = asℝ
317-
asparams(::Affine, ::StaticSymbol{:σ}) = asℝ₊
318-
asparams(::Type{A}, ::StaticSymbol{:μ}) where {A<:Affine} = asℝ
319-
asparams(::Type{A}, ::StaticSymbol{:σ}) where {A<:Affine} = asℝ₊
316+
asparams(::AffinePushfwd, ::StaticSymbol{:μ}) = asℝ
317+
asparams(::AffinePushfwd, ::StaticSymbol{:σ}) = asℝ₊
318+
asparams(::Type{A}, ::StaticSymbol{:μ}) where {A<:AffinePushfwd} = asℝ
319+
asparams(::Type{A}, ::StaticSymbol{:σ}) where {A<:AffinePushfwd} = asℝ₊
320320

321-
function asparams(d::Affine{N,M,T}, ::StaticSymbol{:μ}) where {N,M,T<:AbstractArray}
321+
function asparams(d::AffinePushfwd{N,M,T}, ::StaticSymbol{:μ}) where {N,M,T<:AbstractArray}
322322
as(Array, asℝ, size(d.μ))
323323
end
324324

325-
function asparams(d::Affine{N,M,T}, ::StaticSymbol{:σ}) where {N,M,T<:AbstractArray}
325+
function asparams(d::AffinePushfwd{N,M,T}, ::StaticSymbol{:σ}) where {N,M,T<:AbstractArray}
326326
as(Array, asℝ, size(d.σ))
327327
end
328328

329-
function Base.rand(rng::Random.AbstractRNG, ::Type{T}, d::Affine) where {T}
329+
function Base.rand(rng::Random.AbstractRNG, ::Type{T}, d::AffinePushfwd) where {T}
330330
z = rand(rng, T, parent(d))
331331
f = getfield(d, :f)
332332
return f(z)
333333
end
334334

335-
@inline function insupport(d::Affine, x)
335+
@inline function insupport(d::AffinePushfwd, x)
336336
insupport(d.parent, inverse(d.f)(x))
337337
end
338338

339-
@inline function Distributions.cdf(d::Affine, x)
339+
@inline function Distributions.cdf(d::AffinePushfwd, x)
340340
cdf(parent(d), inverse(d.f)(x))
341341
end
342+
343+
@inline function mean(d::AffinePushfwd)
344+
f = getfield(d, :f)
345+
f(mean(parent(d)))
346+
end
347+
348+
@inline function std(d::AffinePushfwd{(:μ,)})
349+
std(parent(d))
350+
end
351+
352+
@inline function std(d::AffinePushfwd{(:μ, :σ)})
353+
d.σ * std(parent(d))
354+
end
355+
356+
@inline function std(d::AffinePushfwd{(:σ,)})
357+
d.σ * std(parent(d))
358+
end
359+
360+
@inline function std(d::AffinePushfwd{(:λ,)})
361+
std(parent(d)) / d.λ
362+
end
363+
364+
@inline function std(d::AffinePushfwd{(:μ, :λ)})
365+
std(parent(d)) / d.λ
366+
end

src/combinators/transforms.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ as(μ::Pullback) = TV.inverse(μ.f) ∘ μ.ν
9999
as(::Lebesgue) = asℝ
100100

101101
# TODO: Make this work for affine embeddings
102-
as(d::Affine) = _as_affine(_firstval(d))
102+
as(d::AffinePushfwd) = _as_affine(_firstval(d))
103103

104-
_firstval(d::Affine) = first(values(getfield(getfield(d, :f), :par)))
104+
_firstval(d::AffinePushfwd) = first(values(getfield(getfield(d, :f), :par)))
105105
_as_affine(x::Real) = asℝ
106106
_as_affine(x::AbstractArray) = as(Vector, size(x, 1))
107107

src/distributions.jl

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,40 @@ import Distributions
33
export Dists
44
const Dists = Distributions
55

6-
7-
86
@inline function as(d, _data::NamedTuple)
97
if hasmethod(Dists.support, (typeof(d),))
10-
return asTransform(Dists.support(d))
8+
return asTransform(Dists.support(d))
119
end
1210

1311
error("Not implemented:\nas($d)")
1412
end
1513

1614
using TransformVariables: ShiftedExp, ScaledShiftedLogistic
1715

18-
function asTransform(supp:: Dists.RealInterval)
16+
function asTransform(supp::Dists.RealInterval)
1917
(lb, ub) = (supp.lb, supp.ub)
2018

2119
(lb, ub) == (-Inf, Inf) && (return asℝ)
22-
isinf(ub) && return ShiftedExp(true,lb)
23-
isinf(lb) && return ShiftedExp(false,lb)
24-
return ScaledShiftedLogistic(ub-lb, lb)
20+
isinf(ub) && return ShiftedExp(true, lb)
21+
isinf(lb) && return ShiftedExp(false, lb)
22+
return ScaledShiftedLogistic(ub - lb, lb)
2523
end
2624

27-
as::AbstractMeasure, _data::NamedTuple) = as(μ)
25+
as::AbstractMeasure, _data::NamedTuple) = as(μ)
2826

2927
as(d::Dists.AbstractMvNormal, _data::NamedTuple = NamedTuple()) = TV.as(Array, size(d))
3028

31-
32-
function as(d::Dists.Distribution{Dists.Univariate}, _data::NamedTuple=NamedTuple())
29+
function as(d::Dists.Distribution{Dists.Univariate}, _data::NamedTuple = NamedTuple())
3330
sup = Dists.support(d)
3431
lo = isinf(sup.lb) ? -TV.∞ : sup.lb
3532
hi = isinf(sup.ub) ? TV.∞ : sup.ub
36-
as(Real, lo,hi)
33+
as(Real, lo, hi)
3734
end
3835

39-
function as(d::Dists.Product, _data::NamedTuple=NamedTuple())
36+
function as(d::Dists.Product, _data::NamedTuple = NamedTuple())
4037
n = length(d)
4138
v = d.v
4239
as(Vector, as(v[1]), n)
4340
end
4441

45-
as(m::DistributionMeasures.DistributionMeasure) = as(m.d)
42+
as(m::DistributionMeasures.DistributionMeasure) = as(m.d)

src/parameterized.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ function asparams(::Type{M}, constraints::NamedTuple{N}) where {N,M<:Parameteriz
6565
return as(ordered_transforms)
6666
end
6767

68-
# TODO: Make this work for Affine measures
69-
# function asparams(::Type{M}, constraints::NamedTuple{N}) where {N, M<: Affine}
68+
# TODO: Make this work for AffinePushfwd measures
69+
# function asparams(::Type{M}, constraints::NamedTuple{N}) where {N, M<: AffinePushfwd}
7070
# ...
7171
# end
7272

src/parameterized/beta.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ end
2121

2222
@inline function basemeasure(d::Beta{(:α, :β)})
2323
= -logbeta(d.α, d.β)
24-
weightedmeasure(ℓ, Lebesgue())
24+
weightedmeasure(ℓ, LebesgueMeasure())
2525
end
2626

2727
Base.rand(rng::AbstractRNG, T::Type, μ::Beta) = rand(rng, Dists.Beta.α, μ.β))

0 commit comments

Comments
 (0)