From c6680cddf20ee5dac6d074f4db4f3f910dd4473b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20L=C3=BCghausen?= Date: Mon, 24 Jan 2022 17:39:58 +0100 Subject: [PATCH] [python] Add `verbose` deprecation notice and `util.py` - Add a deprecation notice for the optional `verbose` argument - Create a top-level `util.py` to contain non-mathmatical helper functions such as the deprecation warning used in various submodules. --- pypmc/__init__.py | 34 ++++------------------------ pypmc/mix_adapt/hierarchical.py | 14 ++++++------ pypmc/mix_adapt/hierarchical_test.py | 4 ++-- pypmc/mix_adapt/pmc.pyx | 10 ++++---- pypmc/mix_adapt/pmc_test.py | 8 +++---- pypmc/mix_adapt/variational.pyx | 7 +++--- pypmc/mix_adapt/variational_test.py | 10 ++++---- pypmc/tools/_partition.py | 8 +++---- 8 files changed, 35 insertions(+), 60 deletions(-) diff --git a/pypmc/__init__.py b/pypmc/__init__.py index fcd022e..a9704a0 100644 --- a/pypmc/__init__.py +++ b/pypmc/__init__.py @@ -6,34 +6,8 @@ from ._version import __version__ # import these submodules by default -from . import mix_adapt, density, sampler, tools +from . import mix_adapt, density, sampler, tools, util -_log_to_stdout = False -def log_to_stdout(verbose=False): - ''' - Turn on logging and add a handler which prints to stderr, if not active - yet. - - :param verbose: - - Bool; if ``True``, output non-critical status information - - ''' - global _log_to_stdout - import logging - import sys - logger = logging.getLogger(__name__) - - if verbose: - logger.setLevel(logging.INFO) - else: - logger.setLevel(logging.WARNING) - - if not _log_to_stdout: - formatter = logging.Formatter('[%(levelname)s] %(message)s') - handler = logging.StreamHandler(sys.stdout) - handler.setFormatter(formatter) - logger.addHandler(handler) - _log_to_stdout = True - -log_to_stdout() +# Log to stdout per default. The log handler can be removed to use pypmc +# as a library. +util.log_to_stdout() diff --git a/pypmc/mix_adapt/hierarchical.py b/pypmc/mix_adapt/hierarchical.py index 9536fb2..d43dda6 100644 --- a/pypmc/mix_adapt/hierarchical.py +++ b/pypmc/mix_adapt/hierarchical.py @@ -56,7 +56,7 @@ def __init__(self, input_components, initial_guess): # the i-th element is :math:`min_j KL(f_i || g_j)` self.min_kl = np.zeros(self.nin) + np.inf - def _cleanup(self, kill, verbose): + def _cleanup(self, kill): """Look for dead components (weight=0) and remove them if enabled by ``kill``. Resize storage. Recompute determinant and covariance. @@ -175,18 +175,18 @@ def run(self, eps=1e-4, kill=True, max_steps=50, verbose=False): Perform a maximum number of update steps. - :param verbose: - - Output information on progress of algorithm. - """ + if verbose: + from .. import util + util.depr_warn_verbose( __name__) + old_distance = np.finfo(np.float64).max new_distance = np.finfo(np.float64).max logger.info('Starting hierarchical clustering with %d components.' % len(self.g.components)) converged = False for step in range(1, max_steps + 1): - self._cleanup(kill, verbose) + self._cleanup(kill) self._regroup() self._refit() @@ -211,7 +211,7 @@ def run(self, eps=1e-4, kill=True, max_steps=50, verbose=False): # save distance for comparison in next step old_distance = new_distance - self._cleanup(kill, verbose) + self._cleanup(kill) logger.info('%d components remain.' % len(self.g.components)) if converged: diff --git a/pypmc/mix_adapt/hierarchical_test.py b/pypmc/mix_adapt/hierarchical_test.py index 8eff038..a402694 100644 --- a/pypmc/mix_adapt/hierarchical_test.py +++ b/pypmc/mix_adapt/hierarchical_test.py @@ -29,7 +29,7 @@ class TestHierarchical(unittest.TestCase): def test_prune(self): h = Hierarchical(self.input_components1, self.initial_guess1) - h.run(verbose=True) + h.run() sol = h.g # only one component should survive and have weight 1.0 @@ -46,7 +46,7 @@ def test_prune(self): def test_cluster(self): h = Hierarchical(self.input_components2, self.initial_guess2) - h.run(verbose=True) + h.run() sol = h.g # both components should survive and have equal weight diff --git a/pypmc/mix_adapt/pmc.pyx b/pypmc/mix_adapt/pmc.pyx index b6b542a..1a0a74c 100644 --- a/pypmc/mix_adapt/pmc.pyx +++ b/pypmc/mix_adapt/pmc.pyx @@ -390,7 +390,7 @@ class PMC(object): else: return (self.density.multi_evaluate(self.samples) * self.normalized_weights).sum() - def run(self, iterations=1000, prune=0., rel_tol=1e-10, abs_tol=1e-5, verbose=False): + def run(self, iterations=1000, prune=0., rel_tol=1e-10, abs_tol=1e-5, verbose=True): r''' Run PMC updates and check for convergence using the change of the log likelihood of the current and the last step. Convergence is not @@ -429,11 +429,11 @@ class PMC(object): .. math:: \| L_t - L_{t-1} \| < \epsilon_a . - :param verbose: - - Output status information after each update. - ''' + if verbose: + from .. import util + util.depr_warn_verbose( __name__) + old_K = None for i in range(1, iterations + 1): diff --git a/pypmc/mix_adapt/pmc_test.py b/pypmc/mix_adapt/pmc_test.py index 95016e2..38eccaa 100644 --- a/pypmc/mix_adapt/pmc_test.py +++ b/pypmc/mix_adapt/pmc_test.py @@ -390,7 +390,7 @@ def test_invalid_usage(self): def test_adaptation(self): pmc = PMC(self.samples, self.prop, self.weights, latent=self.latent, rb=False) - converged = pmc.run(verbose=True) + converged = pmc.run() outdensity = pmc.density self.assertEqual(converged, 2) @@ -422,7 +422,7 @@ def test_with_overlap(self): samples, latent = target.propose(10**4, trace=True, shuffle=False) pmc = PMC(samples, prop, latent=latent, rb=True) - pmc.run(verbose=True) + pmc.run() adapted_prop = pmc.density adapted_comp_weights = adapted_prop.weights @@ -467,7 +467,7 @@ def test_prune(self): pmc = PMC(samples, prop, rb=True) pmc_prune = 0.5 / len(prop) - converge_step = pmc.run(30, verbose=True, prune=pmc_prune) + converge_step = pmc.run(30, prune=pmc_prune) adapted_prop = pmc.density adapted_comp_weights = adapted_prop.weights @@ -520,7 +520,7 @@ def test_prune(self): pmc = PMC(samples, prop, rb=True, mindof=mindof, maxdof=maxdof) pmc_prune = 0.5 / len(prop) - converge_step = pmc.run(30, verbose=True, prune=pmc_prune) + converge_step = pmc.run(30, prune=pmc_prune) adapted_prop = pmc.density adapted_comp_weights = adapted_prop.weights diff --git a/pypmc/mix_adapt/variational.pyx b/pypmc/mix_adapt/variational.pyx index 4824770..d364360 100644 --- a/pypmc/mix_adapt/variational.pyx +++ b/pypmc/mix_adapt/variational.pyx @@ -315,10 +315,11 @@ class GaussianInference(object): .. math:: \| L_t - L_{t-1} \| < \epsilon_a . - :param verbose: - Output status information after each update. - ''' + if verbose: + from .. import util + util.depr_warn_verbose(__name__) + old_K = None for i in range(1, iterations + 1): # recompute bound in 1st step or if components were removed diff --git a/pypmc/mix_adapt/variational_test.py b/pypmc/mix_adapt/variational_test.py index 45e2f14..ba631d3 100644 --- a/pypmc/mix_adapt/variational_test.py +++ b/pypmc/mix_adapt/variational_test.py @@ -344,7 +344,7 @@ def test_weighted(self): samples = sam.samples[:] clust = GaussianInference(samples, 2, weights=weights, m=np.vstack((prop_mean1,prop_mean2))) - converged = clust.run(verbose=True) + converged = clust.run() self.assertTrue(converged) resulting_mixture = clust.make_mixture() @@ -418,7 +418,7 @@ def test_prune(self): # to result in same numbers, except it works also when # components were reduced => nsteps2 + 1 eps = 1e-15 - nsteps2 = infer2.run(20, verbose=True) + nsteps2 = infer2.run(20) self.assertEqual(nsteps2 + 1, nsteps) result2 = infer2.make_mixture() @@ -493,7 +493,7 @@ def test_initial_guess(self): def test_1d(self): vb = VBMerge(self.input_mix, N=self.N, initial_guess=self.initial_guess) - vb.run(verbose=True) + vb.run() mix = vb.make_mixture() self.assertEqual(len(mix), 1) # means agree as m0 = 0 but correction from beta0 @@ -609,7 +609,7 @@ def test_bimodal(self): # restart, should converge immediately pripos = vb.prior_posterior() vb2 = VBMerge(input_mix, vb.N, **pripos) - nsteps = vb2.run(verbose=True) + nsteps = vb2.run() self.assertEqual(nsteps, 1) self.assertEqual(vb2.likelihood_bound(), vb.likelihood_bound()) @@ -699,7 +699,7 @@ def test_bound(self): # converge exactly in two steps # prune out one component after first update # then get same bound twice - self.assertEqual(vb.run(verbose=True), 2) + self.assertEqual(vb.run(), 2) self.assertEqual(vb.K, 1) res = vb.make_mixture() np.testing.assert_allclose(res.components[0].mu , target_mean, rtol=1e-3) diff --git a/pypmc/tools/_partition.py b/pypmc/tools/_partition.py index a70e22a..bd04b49 100644 --- a/pypmc/tools/_partition.py +++ b/pypmc/tools/_partition.py @@ -48,11 +48,11 @@ def patch_data(data, L=100, try_diag=True, verbose=False): that fails as well, the patch is skipped. If ``False`` the patch is skipped directly. - :param verbose: - - Bool; If ``True`` print all status information. - ''' + if verbose: + from .. import util + util.depr_warn_verbose(__name__) + # patch data into length L patches patches = _np.array([data[patch_start:patch_start + L] for patch_start in range(0, len(data), L)])