Skip to content

Commit

Permalink
[python] Add verbose deprecation notice and util.py
Browse files Browse the repository at this point in the history
- Add a deprecation notice for the optional `verbose` argument
- Create `tools/util.py` to contain technical helpers such as
  the deprecation warning used in various submodules.
  • Loading branch information
pluegh committed Feb 8, 2022
1 parent e4a7c00 commit f3e0e68
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 59 deletions.
32 changes: 3 additions & 29 deletions pypmc/__init__.py
Expand Up @@ -8,32 +8,6 @@
# import these submodules by default
from . import mix_adapt, density, sampler, tools

_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.
tools.util.log_to_stdout()
14 changes: 7 additions & 7 deletions pypmc/mix_adapt/hierarchical.py
Expand Up @@ -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.
Expand Down Expand Up @@ -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 pypmc.tools.util import depr_warn_verbose
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()

Expand All @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions pypmc/mix_adapt/hierarchical_test.py
Expand Up @@ -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
Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions pypmc/mix_adapt/pmc.pyx
Expand Up @@ -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 pypmc.tools.util import depr_warn_verbose
depr_warn_verbose( __name__)

old_K = None

for i in range(1, iterations + 1):
Expand Down
8 changes: 4 additions & 4 deletions pypmc/mix_adapt/pmc_test.py
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions pypmc/mix_adapt/variational.pyx
Expand Up @@ -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 pypmc.tools.util import depr_warn_verbose
depr_warn_verbose( __name__)

old_K = None
for i in range(1, iterations + 1):
# recompute bound in 1st step or if components were removed
Expand Down
10 changes: 5 additions & 5 deletions pypmc/mix_adapt/variational_test.py
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pypmc/tools/__init__.py
Expand Up @@ -5,4 +5,4 @@
from ._history import History
from ._partition import partition, patch_data
from ._plot import plot_mixture, plot_responsibility
from . import indicator, convergence
from . import indicator, convergence, util
8 changes: 4 additions & 4 deletions pypmc/tools/_partition.py
Expand Up @@ -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 pypmc.tools.util import depr_warn_verbose
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)])

Expand Down
38 changes: 38 additions & 0 deletions pypmc/tools/util.py
@@ -0,0 +1,38 @@
import logging

_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

def depr_warn_verbose(logger_name):
logger = logging.getLogger(logger_name)
logger.warn(
f"The optional argument 'verbose' is deprecated and "
f"will be removed in the future. Instead, use the log "
f"level of logging.getLogger({logger_name}) or a parent."
)

0 comments on commit f3e0e68

Please sign in to comment.