You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/source/contributing/developer_guide_implementing_distribution.md
+15-16
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ This guide provides an overview on how to implement a distribution for version 4
4
4
It is designed for developers who wish to add a new distribution to the library.
5
5
Users will not be aware of all this complexity and should instead make use of helper methods such as `~pymc.distributions.DensityDist`.
6
6
7
-
PyMC {class}`~pymc.distributions.Distribution` builds on top of Aesara's {class}`~aesara.tensor.random.op.RandomVariable`, and implements `logp`, `logcdf` and `get_moment` methods as well as other initialization and validation helpers.
7
+
PyMC {class}`~pymc.distributions.Distribution` builds on top of Aesara's {class}`~aesara.tensor.random.op.RandomVariable`, and implements `logp`, `logcdf` and `moment` methods as well as other initialization and validation helpers.
8
8
Most notably `shape/dims` kwargs, alternative parametrizations, and default `transforms`.
9
9
10
10
Here is a summary check-list of the steps needed to implement a new distribution.
@@ -13,7 +13,7 @@ Each section will be expanded below:
13
13
1. Creating a new `RandomVariable``Op`
14
14
1. Implementing the corresponding `Distribution` class
15
15
1. Adding tests for the new `RandomVariable`
16
-
1. Adding tests for `logp` / `logcdf` and `get_moment` methods
16
+
1. Adding tests for `logp` / `logcdf` and `moment` methods
17
17
1. Documenting the new `Distribution`.
18
18
19
19
This guide does not attempt to explain the rationale behind the `Distributions` current implementation, and details are provided only insofar as they help to implement new "standard" distributions.
@@ -119,7 +119,7 @@ After implementing the new `RandomVariable` `Op`, it's time to make use of it in
119
119
PyMC 4.x works in a very {term}`functional <Functional Programming>` way, and the `distribution` classes are there mostly to facilitate porting the `PyMC3` v3.x code to the new `PyMC` v4.x version, add PyMC API features and keep related methods organized together.
120
120
In practice, they take care of:
121
121
122
-
1. Linking ({term}`Dispatching`) a rv_op class with the corresponding `get_moment`, `logp` and `logcdf` methods.
122
+
1. Linking ({term}`Dispatching`) a rv_op class with the corresponding `moment`, `logp` and `logcdf` methods.
123
123
1. Defining a standard transformation (for continuous distributions) that converts a bounded variable domain (e.g., positive line) to an unbounded domain (i.e., the real line), which many samplers prefer.
124
124
1. Validating the parametrization of a distribution and converting non-symbolic inputs (i.e., numeric literals or numpy arrays) to symbolic variables.
125
125
1. Converting multiple alternative parametrizations to the standard parametrization that the `RandomVariable` is defined in terms of.
@@ -154,9 +154,9 @@ class Blah(PositiveContinuous):
154
154
# the rv_op needs in order to be instantiated
155
155
returnsuper().dist([param1, param2], **kwargs)
156
156
157
-
#get_moment returns a symbolic expression for the stable moment from which to start sampling
157
+
#moment returns a symbolic expression for the stable moment from which to start sampling
158
158
# the variable, given the implicit `rv`, `size` and `param1` ... `paramN`
159
-
defget_moment(rv, size, param1, param2):
159
+
defmoment(rv, size, param1, param2):
160
160
moment, _ = at.broadcast_arrays(param1, param2)
161
161
ifnot rv_size_is_none(size):
162
162
moment = at.full(size, moment)
@@ -193,30 +193,29 @@ class Blah(PositiveContinuous):
193
193
194
194
Some notes:
195
195
196
-
1. A distribution should at the very least inherit from {class}`~pymc.distributions.Discrete` or {class}`~pymc.distributions.Continuous`. For the latter, more specific subclasses exist: `PositiveContinuous`, `UnitContinuous`, `BoundedContinuous`, `CircularContinuous`, which specify default transformations for the variables. If you need to specify a one-time custom transform you can also override the `__new__` method, as is done for the {class}`~pymc.distributions.multivariate.Dirichlet`.
197
-
1. If a distribution does not have a corresponding `random` implementation, a `RandomVariable` should still be created that raises a `NotImplementedError`. This is the case for the {class}`~pymc.distributions.continuous.Flat`. In this case it will be necessary to provide a standard `initval` by
198
-
overriding `__new__`.
196
+
1. A distribution should at the very least inherit from {class}`~pymc.distributions.Discrete` or {class}`~pymc.distributions.Continuous`. For the latter, more specific subclasses exist: `PositiveContinuous`, `UnitContinuous`, `BoundedContinuous`, `CircularContinuous`, `SimplexContinuous`, which specify default transformations for the variables. If you need to specify a one-time custom transform you can also create a `_default_transform` dispatch function as is done for the {class}`~pymc.distributions.multivariate.LKJCholeskyCov`.
197
+
1. If a distribution does not have a corresponding `random` implementation, a `RandomVariable` should still be created that raises a `NotImplementedError`. This is the case for the {class}`~pymc.distributions.continuous.Flat`. In this case it will be necessary to provide a `moment` method.
199
198
1. As mentioned above, `PyMC` v4.x works in a very {term}`functional <Functional Programming>` way, and all the information that is needed in the `logp` and `logcdf` methods is expected to be "carried" via the `RandomVariable` inputs. You may pass numerical arguments that are not strictly needed for the `rng_fn` method but are used in the `logp` and `logcdf` methods. Just keep in mind whether this affects the correct shape inference behavior of the `RandomVariable`. If specialized non-numeric information is needed you might need to define your custom`_logp` and `_logcdf` {term}`Dispatching` functions, but this should be done as a last resort.
200
199
1. The `logcdf` method is not a requirement, but it's a nice plus!
201
-
1. Currently only one moment is supported in the `get_moment` method, and probably the "higher-order" one is the most useful (that is `mean` > `median` > `mode`)... You might need to truncate the moment if you are dealing with a discrete distribution.
202
-
1. When creating the `get_moment` method, we have to be careful with `size != None` and broadcast properly when some parameters that are not used in the moment may nevertheless inform about the shape of the distribution. E.g. `pm.Normal.dist(mu=0, sigma=np.arange(1, 6))` returns a moment of `[mu, mu, mu, mu, mu]`.
200
+
1. Currently only one moment is supported in the `moment` method, and probably the "higher-order" one is the most useful (that is `mean` > `median` > `mode`)... You might need to truncate the moment if you are dealing with a discrete distribution.
201
+
1. When creating the `moment` method, we have to be careful with `size != None` and broadcast properly when some parameters that are not used in the moment may nevertheless inform about the shape of the distribution. E.g. `pm.Normal.dist(mu=0, sigma=np.arange(1, 6))` returns a moment of `[mu, mu, mu, mu, mu]`.
203
202
204
203
For a quick check that things are working you can try the following:
205
204
206
205
```python
207
206
208
207
import pymc as pm
209
-
from pymc.distributions.distribution importget_moment
208
+
from pymc.distributions.distribution importmoment
210
209
211
210
# pm.blah = pm.Normal in this example
212
-
blah = pm.blah.dist(mu=0, sigma=1)
211
+
blah = pm.blah.dist(mu=0, sigma=1)
213
212
214
213
# Test that the returned blah_op is still working fine
215
214
blah.eval()
216
215
# array(-1.01397228)
217
216
218
-
# Test the get_moment method
219
-
get_moment(blah).eval()
217
+
# Test the moment method
218
+
moment(blah).eval()
220
219
# array(0.)
221
220
222
221
# Test the logp method
@@ -367,9 +366,9 @@ def test_blah_logcdf(self):
367
366
368
367
```
369
368
370
-
## 5. Adding tests for the `get_moment` method
369
+
## 5. Adding tests for the `moment` method
371
370
372
-
Tests for the `get_moment` method are contained in `pymc/tests/test_distributions_moments.py`, and make use of the function `assert_moment_is_expected`
371
+
Tests for the `moment` method are contained in `pymc/tests/test_distributions_moments.py`, and make use of the function `assert_moment_is_expected`
0 commit comments