From 7f80d6e7f835ac625192a1c2d9af3987c7f7cdf3 Mon Sep 17 00:00:00 2001 From: Junpeng Lao Date: Wed, 16 May 2018 15:34:49 +0200 Subject: [PATCH 1/5] Better error message for Mass matrix contains zeros We see user having a hard time debugging their model when the error `Mass matrix contains zeros on the diagonal. Some derivatives might always be zero` arise. See eg https://discourse.pymc.io/t/unsupervised-clustering-mass-matrix-contains-zeros-on-the-diagonal/1222/. This PR prints out where the error is, so user can easier address the bug by eg changing the scale of those RVs --- RELEASE-NOTES.md | 1 + pymc3/step_methods/hmc/base_hmc.py | 2 +- pymc3/step_methods/hmc/quadpotential.py | 30 +++++++++++++++++++------ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 7ac3cec3af..ea348ae126 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -8,6 +8,7 @@ - Add `check_test_point` method to `pm.Model` - Add `Ordered` Transformation and `OrderedLogistic` distribution +- Better warning message for `Mass matrix contains zeros on the diagonal. Some derivatives might always be zero` ### Fixes diff --git a/pymc3/step_methods/hmc/base_hmc.py b/pymc3/step_methods/hmc/base_hmc.py index d18e28cd0e..68d0627e8c 100644 --- a/pymc3/step_methods/hmc/base_hmc.py +++ b/pymc3/step_methods/hmc/base_hmc.py @@ -112,7 +112,7 @@ def astep(self, q0): start = self.integrator.compute_state(q0, p0) if not np.isfinite(start.energy): - self.potential.raise_ok() + self.potential.raise_ok(self._logp_dlogp_func._ordering.vmap) raise ValueError('Bad initial energy: %s. The model ' 'might be misspecified.' % start.energy) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 1eaf165014..1e5cbc35ed 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -185,15 +185,31 @@ def update(self, sample, grad, tune): self._n_samples += 1 - def raise_ok(self): + def raise_ok(self, vmap): if np.any(self._stds == 0): - raise ValueError('Mass matrix contains zeros on the diagonal. ' - 'Some derivatives might always be zero.') - if np.any(self._stds < 0): - raise ValueError('Mass matrix contains negative values on the ' - 'diagonal.') + name_slc = [] + tmp_hold = list(range(_std.size)) + for vmap_ in vmap: + slclen = len(tmp_hold[vmap_.slc]) + for i in range(slclen): + name_slc.append((i, vmap_.var)) + index = np.where(self._std == 0)[0] + errmsg = ['The derivative of the element index at {} of Variable ' + '`{}` is zero.'.format(*name_slc[ii]) for ii in index] + raise ValueError('Mass matrix contains zeros on the diagonal. ' + + errmsg) if np.any(~np.isfinite(self._stds)): - raise ValueError('Mass matrix contains non-finite values.') + name_slc = [] + tmp_hold = list(range(self._std.size)) + for vmap_ in vmap: + slclen = len(tmp_hold[vmap_.slc]) + for i in range(slclen): + name_slc.append((i, vmap_.var)) + index = np.where(~np.isfinite(self._stds))[0] + errmsg = ['The derivative of the element index at {} of Variable ' + '`{}` is non-finite.'.format(*name_slc[ii]) for ii in index] + raise ValueError('Mass matrix contains non-finite values on the ' + 'diagonal. ' + errmsg) class QuadPotentialDiagAdaptGrad(QuadPotentialDiagAdapt): From 64932c7178db9c1ec0df2e11fe9a3948a8d57ffd Mon Sep 17 00:00:00 2001 From: Junpeng Lao Date: Wed, 16 May 2018 15:41:39 +0200 Subject: [PATCH 2/5] fix bug --- 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 1e5cbc35ed..76b74aab71 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -188,7 +188,7 @@ def update(self, sample, grad, tune): def raise_ok(self, vmap): if np.any(self._stds == 0): name_slc = [] - tmp_hold = list(range(_std.size)) + tmp_hold = list(range(self._std.size)) for vmap_ in vmap: slclen = len(tmp_hold[vmap_.slc]) for i in range(slclen): From 1aef86c1d9f81609bd272f5f5e2cc9d5e183a294 Mon Sep 17 00:00:00 2001 From: Junpeng Lao Date: Wed, 16 May 2018 16:02:29 +0200 Subject: [PATCH 3/5] fix bug --- pymc3/step_methods/hmc/quadpotential.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 76b74aab71..2b0a4fc934 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -188,28 +188,31 @@ def update(self, sample, grad, tune): def raise_ok(self, vmap): if np.any(self._stds == 0): name_slc = [] - tmp_hold = list(range(self._std.size)) + tmp_hold = list(range(self._stds.size)) for vmap_ in vmap: slclen = len(tmp_hold[vmap_.slc]) for i in range(slclen): name_slc.append((i, vmap_.var)) - index = np.where(self._std == 0)[0] - errmsg = ['The derivative of the element index at {} of Variable ' - '`{}` is zero.'.format(*name_slc[ii]) for ii in index] - raise ValueError('Mass matrix contains zeros on the diagonal. ' + - errmsg) + index = np.where(self._stds == 0)[0] + errmsg = ['Mass matrix contains zeros on the diagonal. '] + for ii in index: + errmsg.append('The derivative of the element index at {}' + ' of Variable `{}` is zero.'.format(*name_slc[ii])) + raise ValueError('\n'.join(errmsg)) + if np.any(~np.isfinite(self._stds)): name_slc = [] - tmp_hold = list(range(self._std.size)) + tmp_hold = list(range(self._stds.size)) for vmap_ in vmap: slclen = len(tmp_hold[vmap_.slc]) for i in range(slclen): name_slc.append((i, vmap_.var)) index = np.where(~np.isfinite(self._stds))[0] - errmsg = ['The derivative of the element index at {} of Variable ' - '`{}` is non-finite.'.format(*name_slc[ii]) for ii in index] - raise ValueError('Mass matrix contains non-finite values on the ' - 'diagonal. ' + errmsg) + errmsg = ['Mass matrix contains non-finite values on the diagonal. '] + for ii in index: + errmsg.append('The derivative of the element index at {}' + ' of Variable `{}` is non-finite.'.format(*name_slc[ii])) + raise ValueError('\n'.join(errmsg)) class QuadPotentialDiagAdaptGrad(QuadPotentialDiagAdapt): From d664eff364709da2830c3ccc26ffb4de84a4f2e4 Mon Sep 17 00:00:00 2001 From: Junpeng Lao Date: Wed, 16 May 2018 16:29:35 +0200 Subject: [PATCH 4/5] improve wording of warning --- pymc3/step_methods/hmc/quadpotential.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index 2b0a4fc934..eacfd09d60 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -192,12 +192,12 @@ def raise_ok(self, vmap): for vmap_ in vmap: slclen = len(tmp_hold[vmap_.slc]) for i in range(slclen): - name_slc.append((i, vmap_.var)) + name_slc.append((vmap_.var, i)) index = np.where(self._stds == 0)[0] errmsg = ['Mass matrix contains zeros on the diagonal. '] for ii in index: - errmsg.append('The derivative of the element index at {}' - ' of Variable `{}` is zero.'.format(*name_slc[ii])) + errmsg.append('The derivative of RV `{}`[{}]' + ' is zero.'.format(*name_slc[ii])) raise ValueError('\n'.join(errmsg)) if np.any(~np.isfinite(self._stds)): @@ -206,12 +206,12 @@ def raise_ok(self, vmap): for vmap_ in vmap: slclen = len(tmp_hold[vmap_.slc]) for i in range(slclen): - name_slc.append((i, vmap_.var)) + name_slc.append((vmap_.var, i)) index = np.where(~np.isfinite(self._stds))[0] errmsg = ['Mass matrix contains non-finite values on the diagonal. '] for ii in index: - errmsg.append('The derivative of the element index at {}' - ' of Variable `{}` is non-finite.'.format(*name_slc[ii])) + errmsg.append('The derivative of RV `{}`[{}]' + ' is non-finite.'.format(*name_slc[ii])) raise ValueError('\n'.join(errmsg)) From e2596ac482970637da480908ab401aaafd13c1a5 Mon Sep 17 00:00:00 2001 From: Junpeng Lao Date: Wed, 16 May 2018 17:00:30 +0200 Subject: [PATCH 5/5] improve wording the index is after .ravel() --- pymc3/step_methods/hmc/quadpotential.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pymc3/step_methods/hmc/quadpotential.py b/pymc3/step_methods/hmc/quadpotential.py index eacfd09d60..33a1d73f9f 100644 --- a/pymc3/step_methods/hmc/quadpotential.py +++ b/pymc3/step_methods/hmc/quadpotential.py @@ -196,7 +196,7 @@ def raise_ok(self, vmap): index = np.where(self._stds == 0)[0] errmsg = ['Mass matrix contains zeros on the diagonal. '] for ii in index: - errmsg.append('The derivative of RV `{}`[{}]' + errmsg.append('The derivative of RV `{}`.ravel()[{}]' ' is zero.'.format(*name_slc[ii])) raise ValueError('\n'.join(errmsg)) @@ -210,7 +210,7 @@ def raise_ok(self, vmap): index = np.where(~np.isfinite(self._stds))[0] errmsg = ['Mass matrix contains non-finite values on the diagonal. '] for ii in index: - errmsg.append('The derivative of RV `{}`[{}]' + errmsg.append('The derivative of RV `{}`.ravel()[{}]' ' is non-finite.'.format(*name_slc[ii])) raise ValueError('\n'.join(errmsg))