|
5 | 5 | [](https://github.com/cscherrer/MeasureTheory.jl/actions)
|
6 | 6 | [](https://codecov.io/gh/cscherrer/MeasureTheory.jl)
|
7 | 7 |
|
8 |
| -Check out our [JuliaCon submission](https://github.com/cscherrer/MeasureTheory.jl/blob/paper/paper/paper.pdf) |
9 |
| - |
10 | 8 | `MeasureTheory.jl` is a package for building and reasoning about measures.
|
11 | 9 |
|
12 |
| -# Why? |
| 10 | +## Why? |
13 | 11 |
|
14 |
| -A distribution (as in Distributions.jl) is also called a _probability measure_, and carries with it the constraint of adding (or integrating) to one. Statistical work usually requires this "at the end of the day", but enforcing it at each step of a computation can have considerable overhead. |
| 12 | +A distribution (as provided by `Distributions.jl`) is also called a _probability measure_, and carries with it the constraint of adding (or integrating) to one. Statistical work usually requires this "at the end of the day", but enforcing it at each step of a computation can have considerable overhead. For instance, Bayesian modeling often requires working with unnormalized posterior densities or improper priors. |
15 | 13 |
|
16 | 14 | As a generalization of the concept of volume, measures also have applications outside of probability theory.
|
17 | 15 |
|
18 |
| -# Goals |
19 |
| - |
20 |
| -## Distributions.jl Compatibility |
21 |
| - |
22 |
| -Distirbutions.jl is wildly popular, and is large enough that replacing it all at once would be a major undertaking. |
23 |
| - |
24 |
| -Instead, we should aim to make any Distribution easily usable as a Measure. We'll most likely implement this using an `IsMeasure` trait. |
25 |
| - |
26 |
| -## Absolute Continuity |
27 |
| - |
28 |
| -For two measures μ, ν on a set X, we say μ is _absolutely continuous_ with respect to ν if ν(A)=0 implies μ(A)=0 for every measurable subset A of X. |
29 |
| - |
30 |
| -The following are equivalent: |
31 |
| -1. μ ≪ ν |
32 |
| -2. μ is absolutely continuous wrt ν |
33 |
| -3. There exists a function f such that μ = ∫f dν |
34 |
| - |
35 |
| -So we'll need a `≪` operator. Note that `≪` is not antisymmetric; it's common for both `μ ≪ ν` and `ν ≪ μ` to hold. |
36 |
| - |
37 |
| -If `μ ≪ ν` and `ν ≪ μ`, we say μ and ν are _equivalent_ and write `μ ≃ ν`. (This is often written as `μ ~ ν`, but we reserve `~` for random variables following a distribution, as is common in the literature and probabilistic programming languages.) |
38 |
| - |
39 |
| -If we collapse the equivalence classes (under ≃), `≪` becomes a partial order. |
| 16 | +## Getting started |
40 | 17 |
|
41 |
| -_We need ≃ and ≪ to be fast_. If the support of a measure can be determined statically from its type, we can define ≃ and ≪ as generated functions. |
42 |
| - |
43 |
| -## Radon-Nikodym Derivatives |
44 |
| - |
45 |
| -One of the equivalent conditions above was "There exists a function f such that μ = ∫f dν". In this case, `f` is called a _Radon-Nikodym derivative_, or (less formally) a _density_. In this case we often write `f = dμ/dν`. |
46 |
| - |
47 |
| -For any measures μ and ν with μ≪ν, we should be able to represent this. |
48 |
| - |
49 |
| -## Integration |
50 |
| - |
51 |
| -More generally, we'll need to be able to represent change of measure as above, `∫f dν`. We'll need an `Integral` type |
| 18 | +To install `MeasureTheory.jl`, open the Julia Pkg REPL (by typing `]` in the standard REPL) and run |
52 | 19 |
|
53 | 20 | ```julia
|
54 |
| -struct Integral{F,M} |
55 |
| - f::F |
56 |
| - μ::M |
57 |
| -end |
| 21 | +pkg> add MeasureTheory |
58 | 22 | ```
|
59 | 23 |
|
60 |
| -Then we'll have a function `∫`. For cases where μ = ∫f dν, `∫(f, ν)` will just return `μ` (we can do this based on the types). For unknown cases (which will be most of them), we'll return `∫(f, ν) = Integral(f, ν)`. |
61 |
| - |
62 |
| -## Measure Combinators |
63 |
| - |
64 |
| -It should be very easy to build new measures from existing ones. This can be done using, for example, |
65 |
| - |
66 |
| -- restriction |
67 |
| -- product measure |
68 |
| -- superposition |
69 |
| -- pushforward |
70 |
| - |
71 |
| -There's also function spaces, but this gets much trickier. We'll need to determine a good way to reason about this. |
72 |
| - |
73 |
| -## More??? |
74 |
| - |
75 |
| -This is very much a work in progress. If there are things you think we should have as goals, please add an issue with the `goals` label. |
76 |
| - |
77 |
| - |
78 |
| ------------------- |
79 |
| -# Old Stuff |
80 |
| - |
81 |
| -**WARNING: The current README is very developer-oriented. Casual use will be much simpler** |
82 |
| - |
83 |
| -For an example, let's walk through the construction of `src/probability/Normal`. |
84 |
| - |
85 |
| -First, we have |
86 |
| - |
87 |
| -```julia |
88 |
| -@measure Normal |
89 |
| -``` |
90 |
| - |
91 |
| -this is just a little helper function, and is equivalent to |
92 |
| - |
93 |
| -# TODO: Clean up |
94 |
| -```julia |
95 |
| -quote |
96 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:55 =# |
97 |
| - struct Normal{var"#10#P", var"#11#X"} <: Measures.AbstractMeasure{var"#11#X"} |
98 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:56 =# |
99 |
| - par::var"#10#P" |
100 |
| - end |
101 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:59 =# |
102 |
| - function Normal(var"#13#nt"::Measures.NamedTuple) |
103 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:59 =# |
104 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:60 =# |
105 |
| - var"#12#P" = Measures.typeof(var"#13#nt") |
106 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:61 =# |
107 |
| - return Normal{var"#12#P", Measures.eltype(Normal{var"#12#P"})} |
108 |
| - end |
109 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:64 =# |
110 |
| - Normal(; var"#14#kwargs"...) = begin |
111 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:64 =# |
112 |
| - Normal((; var"#14#kwargs"...)) |
113 |
| - end |
114 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:66 =# |
115 |
| - (var"#8#basemeasure"(var"#15#μ"::Normal{var"#16#P", var"#17#X"}) where {var"#16#P", var"#17#X"}) = begin |
116 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:66 =# |
117 |
| - Lebesgue{var"#17#X"} |
118 |
| - end |
119 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:68 =# |
120 |
| - (var"#9#≪"(::Normal{var"#19#P", var"#20#X"}, ::Lebesgue{var"#20#X"}) where {var"#19#P", var"#20#X"}) = begin |
121 |
| - #= /home/chad/git/Measures.jl/src/Measures.jl:68 =# |
122 |
| - true |
123 |
| - end |
124 |
| -end |
125 |
| -``` |
126 |
| - |
127 |
| -Next we have |
128 |
| - |
129 |
| -```julia |
130 |
| -Normal(μ::Real, σ::Real) = Normal(μ=μ, σ=σ) |
131 |
| -``` |
| 24 | +To get an idea of the possibilities offered by this package, go to the [documentation](https://cscherrer.github.io/MeasureTheory.jl/stable). |
132 | 25 |
|
133 |
| -This defines a default. If we just give two numbers as arguments (but no names), we'll assume this parameterization. |
134 |
| - |
135 |
| -Next need to define a `eltype` function. This takes a constructor (here `Normal`) and a parameter, and tells us the space for which this defines a measure. Let's define this in terms of the types of the parameters, |
136 |
| - |
137 |
| -```julia |
138 |
| -eltype(::Type{Normal}, ::Type{NamedTuple{(:μ, :σ), Tuple{A, B}}}) where {A,B} = promote_type(A,B) |
139 |
| -``` |
140 |
| - |
141 |
| -That's still kind of boring, so let's build the density. For this, we need to implement the trait |
142 |
| - |
143 |
| -```julia |
144 |
| -@trait Density{M,X} where {X = domain{M}} begin |
145 |
| - basemeasure :: [M] => Measure{X} |
146 |
| - logdensity :: [M, X] => Real |
147 |
| -end |
148 |
| -``` |
149 |
| - |
150 |
| -A density doesn't exist by itself, but is defined relative to some _base measure_. For a normal distribution this is just Lebesgue measure on the real numbers. That, together with the usual Gaussian log-density, gives us |
151 |
| - |
152 |
| -```julia |
153 |
| -@implement Density{Normal{X,P},X} where {X, P <: NamedTuple{(:μ, :σ)}} begin |
154 |
| - basemeasure(d) = Lebesgue(X) |
155 |
| - logdensity(d, x) = - (log(2) + log(π)) / 2 - log(d.par.σ) - (x - d.par.μ)^2 / (2 * d.par.σ^2) |
156 |
| -end |
157 |
| -``` |
158 |
| - |
159 |
| -Now we can compute the log-density: |
160 |
| - |
161 |
| -```julia |
162 |
| -julia> logdensity(Normal(0.0, 0.5), 1.0) |
163 |
| --2.2257913526447273 |
164 |
| -``` |
165 |
| - |
166 |
| -And just to check that our default is working, |
167 |
| - |
168 |
| -```julia |
169 |
| -julia> logdensity(Normal(μ=0.0, σ=0.5), 1.0) |
170 |
| --2.2257913526447273 |
171 |
| -``` |
172 |
| - |
173 |
| -What about other parameterizations? Sure, no problem. Here's a way to write this for mean `μ` (as before), but using the _precision_ (inverse of the variance) instead of standard deviation: |
174 |
| - |
175 |
| -```julia |
176 |
| -eltype(::Type{Normal}, ::Type{NamedTuple{(:μ, :τ), Tuple{A, B}}}) where {A,B} = promote_type(A,B) |
177 |
| - |
178 |
| -@implement Density{Normal{X,P},X} where {X, P <: NamedTuple{(:μ, :τ)}} begin |
179 |
| - basemeasure(d) = Lebesgue(X) |
180 |
| - logdensity(d, x) = - (log(2) + log(π) - log(d.par.τ) + d.par.τ * (x - d.par.μ)^2) / 2 |
181 |
| -end |
182 |
| -``` |
183 |
| - |
184 |
| -And another check: |
185 |
| - |
186 |
| -```julia |
187 |
| -julia> logdensity(Normal(μ=0.0, τ=4.0), 1.0) |
188 |
| --2.2257913526447273 |
189 |
| -``` |
190 |
| - |
191 |
| -We can combine measures in a few ways, for now just _scaling_ and _superposition_: |
192 |
| - |
193 |
| -```julia |
194 |
| -julia> 2.0*Lebesgue(Float64) + Normal(0.0,1.0) |
195 |
| -SuperpositionMeasure{Float64,2}((MeasureTheory.WeightedMeasure{Float64,Float64}(2.0, Lebesgue{Float64}()), Normal{NamedTuple{(:μ, :σ),Tuple{Float64,Float64}},Float64}((μ = 0.0, σ = 1.0)))) |
196 |
| -``` |
197 |
| - |
198 |
| ---- |
199 |
| - |
200 |
| -For an easy way to find expressions for the common log-densities, see [this gist](https://gist.github.com/cscherrer/47f0fc7597b4ffc11186d54cc4d8e577) |
| 26 | +To know more about the underlying theory and its applications to probabilistic programming, check out our [JuliaCon 2021 submission](https://arxiv.org/abs/2110.00602). |
201 | 27 |
|
202 | 28 | ## Support
|
| 29 | + |
203 | 30 | [<img src=https://user-images.githubusercontent.com/1184449/140397787-9b7e3eb7-49cd-4c63-8f3c-e5cdc41e393d.png width="49%">](https://informativeprior.com/) [<img src=https://planting.space/sponsor/PlantingSpace-sponsor-3.png width=49%>](https://planting.space)
|
204 | 31 |
|
205 | 32 | ## Stargazers over time
|
|
0 commit comments