From 4f68dfdb64d1f2c86451fa19cf1ef54b8a9c8b4e Mon Sep 17 00:00:00 2001 From: Fearghus Robert Keeble Date: Tue, 31 Mar 2020 14:34:01 +0100 Subject: [PATCH 1/7] Added wrapper for gamma sampling in negative binomial distribution. --- pymc3/distributions/discrete.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pymc3/distributions/discrete.py b/pymc3/distributions/discrete.py index fabaa1dd8f..eea166c459 100644 --- a/pymc3/distributions/discrete.py +++ b/pymc3/distributions/discrete.py @@ -673,12 +673,24 @@ def random(self, point=None, size=None): array """ mu, alpha = draw_values([self.mu, self.alpha], point=point, size=size) - g = generate_samples(stats.gamma.rvs, alpha, scale=mu / alpha, + g = generate_samples(self._random, mu=mu, alpha=alpha, dist_shape=self.shape, size=size) g[g == 0] = np.finfo(float).eps # Just in case return np.asarray(stats.poisson.rvs(g)).reshape(g.shape) + def _random(self, mu, alpha, size): + """ Wrapper around stats.gamma.rvs that converts NegativeBinomial's + parametrization to scipy.gamma. All parameter arrays should have + been broadcasted properly by generate_samples at this point and size is + the scipy.rvs representation. + """ + return stats.gamma.rvs( + a=alpha, + scale=mu / alpha, + size=size, + ) + def logp(self, value): """ Calculate log-probability of NegativeBinomial distribution at specified value. From 022f571d8eb08204af54d45f4c0a680309bb3f86 Mon Sep 17 00:00:00 2001 From: Fearghus Robert Keeble Date: Tue, 31 Mar 2020 14:59:49 +0100 Subject: [PATCH 2/7] Fixed typo in posterior sampling warnings. --- pymc3/sampling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymc3/sampling.py b/pymc3/sampling.py index 8ecce6828b..4f7632564a 100644 --- a/pymc3/sampling.py +++ b/pymc3/sampling.py @@ -1569,9 +1569,9 @@ def sample_posterior_predictive( nchain = 1 if keep_size and samples is not None: - raise IncorrectArgumentsError("Should not specify both keep_size and samples argukments") + raise IncorrectArgumentsError("Should not specify both keep_size and samples arguments") if keep_size and size is not None: - raise IncorrectArgumentsError("Should not specify both keep_size and size argukments") + raise IncorrectArgumentsError("Should not specify both keep_size and size arguments") if samples is None: if isinstance(trace, MultiTrace): From bf8552e27eaae8383465b29e107627280adb4d6b Mon Sep 17 00:00:00 2001 From: Alexandre ANDORRA Date: Tue, 31 Mar 2020 19:40:19 +0200 Subject: [PATCH 3/7] Drop nuts init method from pm.sample (#3863) * Dropped nuts init method * Dropped nuts init method from tests * Refined doc string and added release note --- RELEASE-NOTES.md | 1 + pymc3/sampling.py | 16 ++-------------- pymc3/tests/test_sampling.py | 3 +-- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 50f95be271..6fc30bad59 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -18,6 +18,7 @@ - Deprecated `sd` in version 3.7 has been replaced by `sigma` now raises `DepreciationWarning` on using `sd` in continuous, mixed and timeseries distributions. (see #3837 and #3688). - In named models, `pm.Data` objects now get model-relative names (see [#3843](https://github.com/pymc-devs/pymc3/pull/3843)). - `pm.sample` now takes 1000 draws and 1000 tuning samples by default, instead of 500 previously (see [#3855](https://github.com/pymc-devs/pymc3/pull/3855)). +- Dropped the outdated 'nuts' initialization method for `pm.sample` (see [#3863](https://github.com/pymc-devs/pymc3/pull/3863)). ## PyMC3 3.8 (November 29 2019) diff --git a/pymc3/sampling.py b/pymc3/sampling.py index 8ecce6828b..eab382ce87 100644 --- a/pymc3/sampling.py +++ b/pymc3/sampling.py @@ -274,7 +274,6 @@ def sample( * advi: Run ADVI to estimate posterior mean and diagonal mass matrix. * advi_map: Initialize ADVI with MAP and use MAP as starting point. * map: Use the MAP as starting point. This is discouraged. - * nuts: Run NUTS and estimate posterior mean and mass matrix from the trace. * adapt_full: Adapt a dense mass matrix using the sample covariances step: function or iterable of functions A step function or collection of functions. If there are variables without step methods, @@ -282,8 +281,7 @@ def sample( method will be used, if appropriate to the model; this is a good default for beginning users. n_init: int - Number of iterations of initializer. Only works for 'nuts' and 'ADVI'. - If 'ADVI', number of iterations, if 'nuts', number of draws. + Number of iterations of initializer. Only works for 'ADVI' init methods. start: dict, or array of dict Starting point in parameter space (or partial point) Defaults to ``trace.point(-1))`` if there is a trace provided and model.test_point if not @@ -1865,14 +1863,11 @@ def init_nuts( * advi: Run ADVI to estimate posterior mean and diagonal mass matrix. * advi_map: Initialize ADVI with MAP and use MAP as starting point. * map: Use the MAP as starting point. This is discouraged. - * nuts: Run NUTS and estimate posterior mean and mass matrix from - the trace. * adapt_full: Adapt a dense mass matrix using the sample covariances chains: int Number of jobs to start. n_init: int - Number of iterations of initializer - If 'ADVI', number of iterations, if 'nuts', number of draws. + Number of iterations of initializer. Only works for 'ADVI' init methods. model: Model (optional if in ``with`` context) progressbar: bool Whether or not to display a progressbar for advi sampling. @@ -2001,13 +1996,6 @@ def init_nuts( cov = pm.find_hessian(point=start) start = [start] * chains potential = quadpotential.QuadPotentialFull(cov) - elif init == "nuts": - init_trace = pm.sample( - draws=n_init, step=pm.NUTS(), tune=n_init // 2, random_seed=random_seed - ) - cov = np.atleast_1d(pm.trace_cov(init_trace)) - start = list(np.random.choice(init_trace, chains)) - potential = quadpotential.QuadPotentialFull(cov) elif init == "adapt_full": start = [model.test_point] * chains mean = np.mean([model.dict_to_array(vals) for vals in start], axis=0) diff --git a/pymc3/tests/test_sampling.py b/pymc3/tests/test_sampling.py index 6c0eece037..527c944ab7 100644 --- a/pymc3/tests/test_sampling.py +++ b/pymc3/tests/test_sampling.py @@ -87,7 +87,7 @@ def test_sample(self): def test_sample_init(self): with self.model: - for init in ("advi", "advi_map", "map", "nuts"): + for init in ("advi", "advi_map", "map"): pm.sample( init=init, tune=0, @@ -675,7 +675,6 @@ def test_sample_posterior_predictive_w(self): "advi+adapt_diag_grad", "map", "advi_map", - "nuts", ], ) def test_exec_nuts_init(method): From 27f46830f9e6e4a3944583591acc0fc0de31e7a7 Mon Sep 17 00:00:00 2001 From: Fearghus Robert Keeble Date: Tue, 31 Mar 2020 14:34:01 +0100 Subject: [PATCH 4/7] Added wrapper for gamma sampling in negative binomial distribution. --- pymc3/distributions/discrete.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pymc3/distributions/discrete.py b/pymc3/distributions/discrete.py index fabaa1dd8f..eea166c459 100644 --- a/pymc3/distributions/discrete.py +++ b/pymc3/distributions/discrete.py @@ -673,12 +673,24 @@ def random(self, point=None, size=None): array """ mu, alpha = draw_values([self.mu, self.alpha], point=point, size=size) - g = generate_samples(stats.gamma.rvs, alpha, scale=mu / alpha, + g = generate_samples(self._random, mu=mu, alpha=alpha, dist_shape=self.shape, size=size) g[g == 0] = np.finfo(float).eps # Just in case return np.asarray(stats.poisson.rvs(g)).reshape(g.shape) + def _random(self, mu, alpha, size): + """ Wrapper around stats.gamma.rvs that converts NegativeBinomial's + parametrization to scipy.gamma. All parameter arrays should have + been broadcasted properly by generate_samples at this point and size is + the scipy.rvs representation. + """ + return stats.gamma.rvs( + a=alpha, + scale=mu / alpha, + size=size, + ) + def logp(self, value): """ Calculate log-probability of NegativeBinomial distribution at specified value. From de7aaf002b8dd2b1ca2875fb77892306eb541f10 Mon Sep 17 00:00:00 2001 From: Fearghus Robert Keeble Date: Tue, 31 Mar 2020 14:59:49 +0100 Subject: [PATCH 5/7] Fixed typo in posterior sampling warnings. --- pymc3/sampling.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymc3/sampling.py b/pymc3/sampling.py index eab382ce87..47b9b0d589 100644 --- a/pymc3/sampling.py +++ b/pymc3/sampling.py @@ -1567,9 +1567,9 @@ def sample_posterior_predictive( nchain = 1 if keep_size and samples is not None: - raise IncorrectArgumentsError("Should not specify both keep_size and samples argukments") + raise IncorrectArgumentsError("Should not specify both keep_size and samples arguments") if keep_size and size is not None: - raise IncorrectArgumentsError("Should not specify both keep_size and size argukments") + raise IncorrectArgumentsError("Should not specify both keep_size and size arguments") if samples is None: if isinstance(trace, MultiTrace): From 7d17fdaff822d99737205c3cd66a7292edf4b0ed Mon Sep 17 00:00:00 2001 From: Fearghus Robert Keeble Date: Wed, 1 Apr 2020 11:00:00 +0100 Subject: [PATCH 6/7] Added test for negative binomial input shape. --- pymc3/tests/test_distributions_random.py | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pymc3/tests/test_distributions_random.py b/pymc3/tests/test_distributions_random.py index ef8994e716..393af88ad8 100644 --- a/pymc3/tests/test_distributions_random.py +++ b/pymc3/tests/test_distributions_random.py @@ -1176,6 +1176,31 @@ def sample_prior( with model: return pm.sample_prior_predictive(prior_samples) + @pytest.mark.parametrize( + ["prior_samples", "shape", "mu", "alpha"], + [ + [10, (3,), (None, tuple()), (None, (3,))], + [10, (3,), (None, (3,)), (None, tuple())], + [10, (4, 3,), (None, (3,)), (None, (3,))], + [10, (4, 3,), (None, (3,)), (None, (4, 3))], + ], + ids=str, + ) + def test_NegativeBinomial( + self, + prior_samples, + shape, + mu, + alpha, + ): + prior = self.sample_prior( + distribution=pm.NegativeBinomial, + shape=shape, + nested_rvs_info=dict(mu=mu, alpha=alpha), + prior_samples=prior_samples, + ) + assert prior["target"].shape == (prior_samples,) + shape + @pytest.mark.parametrize( ["prior_samples", "shape", "psi", "mu", "alpha"], [ From d3b8dc28538585d6ab4d01077608d95ee8e6460f Mon Sep 17 00:00:00 2001 From: Fearghus Robert Keeble Date: Wed, 1 Apr 2020 11:05:07 +0100 Subject: [PATCH 7/7] Updating release notes with changes to negative binomial sampling. --- RELEASE-NOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 6fc30bad59..10639c14db 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -19,6 +19,7 @@ - In named models, `pm.Data` objects now get model-relative names (see [#3843](https://github.com/pymc-devs/pymc3/pull/3843)). - `pm.sample` now takes 1000 draws and 1000 tuning samples by default, instead of 500 previously (see [#3855](https://github.com/pymc-devs/pymc3/pull/3855)). - Dropped the outdated 'nuts' initialization method for `pm.sample` (see [#3863](https://github.com/pymc-devs/pymc3/pull/3863)). +- Moved argument division out of `NegativeBinomial` `random` method. Fixes [#3864](https://github.com/pymc-devs/pymc3/issues/3864) in the style of [#3509](https://github.com/pymc-devs/pymc3/pull/3509). ## PyMC3 3.8 (November 29 2019)