From 04b6c862605961e5b7c1c7c607af187e35e41dd9 Mon Sep 17 00:00:00 2001 From: George Ho <19851673+eigenfoo@users.noreply.github.com> Date: Tue, 3 Dec 2019 17:46:15 -0800 Subject: [PATCH 1/8] Add adaptation window multiplier to full mass matrix --- pymc3/step_methods/hmc/quadpotential.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 65ae08d969..8184dd07fe 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -472,6 +472,7 @@ def __init__( initial_cov=None, initial_weight=0, adaptation_window=101, + adaptation_window_multiplier=1.0, update_window=1, doubling=True, dtype=None, @@ -513,6 +514,7 @@ def __init__( self._doubling = doubling self._adaptation_window = int(adaptation_window) + self._adaptation_window_multiplier = int(adaptation_window_multiplier) self._update_window = int(update_window) self._previous_update = 0 @@ -538,7 +540,8 @@ def update(self, sample, grad, tune): if (delta + 1) % self._update_window == 0: self._update_from_weightvar(self._foreground_cov) - # Reset the background covariance if we are at the end of the adaptation window. + # Reset the background covariance if we are at the end of the adaptation + # window. if delta >= self._adaptation_window: self._foreground_cov = self._background_cov self._background_cov = _WeightedCovariance( @@ -547,7 +550,7 @@ def update(self, sample, grad, tune): self._previous_update = self._n_samples if self._doubling: - self._adaptation_window *= 2 + self._adaptation_window *= self._adaptation_window_multiplier self._n_samples += 1 From 25d4bc79cbb72f2d972f8fa7832a3bd04aa55e21 Mon Sep 17 00:00:00 2001 From: George Ho <19851673+eigenfoo@users.noreply.github.com> Date: Tue, 3 Dec 2019 17:53:00 -0800 Subject: [PATCH 2/8] Add adaptation window multiplier to diag adapt --- pymc3/step_methods/hmc/quadpotential.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 8184dd07fe..22b1e1d273 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -126,8 +126,16 @@ def isquadpotential(value): class QuadPotentialDiagAdapt(QuadPotential): """Adapt a diagonal mass matrix from the sample variances.""" - def __init__(self, n, initial_mean, initial_diag=None, initial_weight=0, - adaptation_window=101, dtype=None): + def __init__( + self, + n, + initial_mean, + initial_diag=None, + initial_weight=0, + adaptation_window=101, + adaptation_window_multiplier=1, + dtype=None, + ): """Set up a diagonal mass matrix.""" if initial_diag is not None and initial_diag.ndim != 1: raise ValueError('Initial diagonal must be one-dimensional.') @@ -158,6 +166,7 @@ def __init__(self, n, initial_mean, initial_diag=None, initial_weight=0, self._background_var = _WeightedVariance(self._n, dtype=self.dtype) self._n_samples = 0 self.adaptation_window = adaptation_window + self.adaptation_window_multiplier = int(adaptation_window_multiplier) def velocity(self, x, out=None): """Compute the current velocity at a position in parameter space.""" @@ -199,6 +208,7 @@ def update(self, sample, grad, tune): if self._n_samples > 0 and self._n_samples % window == 0: self._foreground_var = self._background_var self._background_var = _WeightedVariance(self._n, dtype=self.dtype) + self.adaptation_window *= self.adaptation_window_multiplier self._n_samples += 1 From 0f5edc3ec09ebb64bb53fb68ba1d684f03bf20ab Mon Sep 17 00:00:00 2001 From: George Ho <19851673+eigenfoo@users.noreply.github.com> Date: Tue, 3 Dec 2019 17:53:59 -0800 Subject: [PATCH 3/8] Set default value of adaptatation window multiplier to be an int --- pymc3/step_methods/hmc/quadpotential.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 22b1e1d273..9bcb90b40c 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -482,7 +482,7 @@ def __init__( initial_cov=None, initial_weight=0, adaptation_window=101, - adaptation_window_multiplier=1.0, + adaptation_window_multiplier=1, update_window=1, doubling=True, dtype=None, From 4d34222a502139f37f92408ad1408d83bedce571 Mon Sep 17 00:00:00 2001 From: George Ho <19851673+eigenfoo@users.noreply.github.com> Date: Tue, 3 Dec 2019 18:08:38 -0800 Subject: [PATCH 4/8] Clean up extraneous variable --- pymc3/step_methods/hmc/quadpotential.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 9bcb90b40c..9a83614b2d 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -199,13 +199,11 @@ def update(self, sample, grad, tune): if not tune: return - window = self.adaptation_window - self._foreground_var.add_sample(sample, weight=1) self._background_var.add_sample(sample, weight=1) self._update_from_weightvar(self._foreground_var) - if self._n_samples > 0 and self._n_samples % window == 0: + if self._n_samples > 0 and self._n_samples % self.adaptation_window == 0: self._foreground_var = self._background_var self._background_var = _WeightedVariance(self._n, dtype=self.dtype) self.adaptation_window *= self.adaptation_window_multiplier From 5d28bbd9447ae3c37aba48318dd47d8e673c94fd Mon Sep 17 00:00:00 2001 From: George Ho <19851673+eigenfoo@users.noreply.github.com> Date: Tue, 3 Dec 2019 19:05:10 -0800 Subject: [PATCH 5/8] Fix tests with multiplier --- pymc3/tests/test_quadpotential.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pymc3/tests/test_quadpotential.py b/pymc3/tests/test_quadpotential.py index a22ece3f09..5b69cd6f91 100644 --- a/pymc3/tests/test_quadpotential.py +++ b/pymc3/tests/test_quadpotential.py @@ -225,7 +225,7 @@ def test_full_adapt_adaptation_window(seed=8978): for i in range(window + 1): pot.update(np.random.randn(2), None, True) assert pot._previous_update == window - assert pot._adaptation_window == window * 2 + assert pot._adaptation_window == window * pot._adaptation_window_multiplier pot = quadpotential.QuadPotentialFullAdapt( 2, np.zeros(2), np.eye(2), 1, adaptation_window=window, doubling=False From 944b253cfd0862088168eb9c39413a77adcf0bfb Mon Sep 17 00:00:00 2001 From: George Ho <19851673+eigenfoo@users.noreply.github.com> Date: Tue, 3 Dec 2019 21:01:38 -0800 Subject: [PATCH 6/8] Update docstring and remove extraneous parameter --- pymc3/step_methods/hmc/quadpotential.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 9a83614b2d..114fd8cee0 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -466,13 +466,7 @@ def velocity_energy(self, x, v_out): class QuadPotentialFullAdapt(QuadPotentialFull): - """Adapt a dense mass matrix using the sample covariances - - If the parameter ``doubling`` is true, the adaptation window is doubled - every time it is passed. This can lead to better convergence of the mass - matrix estimation. - - """ + """Adapt a dense mass matrix using the sample covariances.""" def __init__( self, n, @@ -480,9 +474,8 @@ def __init__( initial_cov=None, initial_weight=0, adaptation_window=101, - adaptation_window_multiplier=1, + adaptation_window_multiplier=2, update_window=1, - doubling=True, dtype=None, ): warnings.warn("QuadPotentialFullAdapt is an experimental feature") @@ -520,7 +513,6 @@ def __init__( self._background_cov = _WeightedCovariance(self._n, dtype=self.dtype) self._n_samples = 0 - self._doubling = doubling self._adaptation_window = int(adaptation_window) self._adaptation_window_multiplier = int(adaptation_window_multiplier) self._update_window = int(update_window) From dc17cc9a7fe547443c9ec9810f060d325cb24034 Mon Sep 17 00:00:00 2001 From: George Ho <19851673+eigenfoo@users.noreply.github.com> Date: Tue, 3 Dec 2019 22:01:41 -0800 Subject: [PATCH 7/8] Fix tests --- pymc3/step_methods/hmc/quadpotential.py | 3 +-- pymc3/tests/test_quadpotential.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 114fd8cee0..1f3252efe5 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -549,8 +549,7 @@ def update(self, sample, grad, tune): ) self._previous_update = self._n_samples - if self._doubling: - self._adaptation_window *= self._adaptation_window_multiplier + self._adaptation_window *= self._adaptation_window_multiplier self._n_samples += 1 diff --git a/pymc3/tests/test_quadpotential.py b/pymc3/tests/test_quadpotential.py index 5b69cd6f91..7b5e62050d 100644 --- a/pymc3/tests/test_quadpotential.py +++ b/pymc3/tests/test_quadpotential.py @@ -228,12 +228,12 @@ def test_full_adapt_adaptation_window(seed=8978): assert pot._adaptation_window == window * pot._adaptation_window_multiplier pot = quadpotential.QuadPotentialFullAdapt( - 2, np.zeros(2), np.eye(2), 1, adaptation_window=window, doubling=False + 2, np.zeros(2), np.eye(2), 1, adaptation_window=window ) for i in range(window + 1): pot.update(np.random.randn(2), None, True) assert pot._previous_update == window - assert pot._adaptation_window == window + assert pot._adaptation_window == window * pot._adaptation_window_multiplier def test_full_adapt_not_invertible(): From b1aecf8b98262bb22fc71afad24f2d7c811b3095 Mon Sep 17 00:00:00 2001 From: George Ho <19851673+eigenfoo@users.noreply.github.com> Date: Wed, 4 Dec 2019 08:31:29 -0800 Subject: [PATCH 8/8] Make multiplier float and cast to int after multiplying --- pymc3/step_methods/hmc/quadpotential.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 1f3252efe5..886dd5f98b 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -166,7 +166,7 @@ def __init__( self._background_var = _WeightedVariance(self._n, dtype=self.dtype) self._n_samples = 0 self.adaptation_window = adaptation_window - self.adaptation_window_multiplier = int(adaptation_window_multiplier) + self.adaptation_window_multiplier = float(adaptation_window_multiplier) def velocity(self, x, out=None): """Compute the current velocity at a position in parameter space.""" @@ -206,7 +206,7 @@ def update(self, sample, grad, tune): if self._n_samples > 0 and self._n_samples % self.adaptation_window == 0: self._foreground_var = self._background_var self._background_var = _WeightedVariance(self._n, dtype=self.dtype) - self.adaptation_window *= self.adaptation_window_multiplier + self.adaptation_window = int(self.adaptation_window * self.adaptation_window_multiplier) self._n_samples += 1 @@ -514,7 +514,7 @@ def __init__( self._n_samples = 0 self._adaptation_window = int(adaptation_window) - self._adaptation_window_multiplier = int(adaptation_window_multiplier) + self._adaptation_window_multiplier = float(adaptation_window_multiplier) self._update_window = int(update_window) self._previous_update = 0 @@ -549,7 +549,7 @@ def update(self, sample, grad, tune): ) self._previous_update = self._n_samples - self._adaptation_window *= self._adaptation_window_multiplier + self._adaptation_window = int(self._adaptation_window * self._adaptation_window_multiplier) self._n_samples += 1