From a9950628e82dd62d945586dd5c54b2365e0c318c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20L=C3=BCghausen?= Date: Mon, 24 Jan 2022 16:44:18 +0100 Subject: [PATCH] [python] Use logger instead of print --- pypmc/__init__.py | 30 ++++++++++++++++++++++++++++++ pypmc/mix_adapt/hierarchical.py | 23 +++++++++++------------ pypmc/mix_adapt/pmc.pyx | 18 +++++++++--------- pypmc/mix_adapt/variational.pyx | 20 ++++++++++---------- pypmc/sampler/markov_chain.py | 9 ++++++--- pypmc/tools/_partition.py | 14 +++++++------- 6 files changed, 73 insertions(+), 41 deletions(-) diff --git a/pypmc/__init__.py b/pypmc/__init__.py index a97fae7..fcd022e 100644 --- a/pypmc/__init__.py +++ b/pypmc/__init__.py @@ -7,3 +7,33 @@ # 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() diff --git a/pypmc/mix_adapt/hierarchical.py b/pypmc/mix_adapt/hierarchical.py index b30b5e1..9536fb2 100644 --- a/pypmc/mix_adapt/hierarchical.py +++ b/pypmc/mix_adapt/hierarchical.py @@ -5,6 +5,9 @@ import numpy as np import scipy.linalg as linalg +import logging +logger = logging.getLogger(__name__) + class Hierarchical(object): """Hierarchical clustering as described in [GR04]_. @@ -64,8 +67,8 @@ def _cleanup(self, kill, verbose): self.nout -= len(removed_indices) - if verbose and removed_indices: - print('Removing %s' % removed_indices) + if removed_indices: + logger.info('Removing %s' % removed_indices) for j in removed_indices: self.inv_map.pop(j[0]) @@ -180,8 +183,7 @@ def run(self, eps=1e-4, kill=True, max_steps=50, verbose=False): old_distance = np.finfo(np.float64).max new_distance = np.finfo(np.float64).max - if verbose: - print('Starting hierarchical clustering with %d components.' % len(self.g.components)) + 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) @@ -191,12 +193,10 @@ def run(self, eps=1e-4, kill=True, max_steps=50, verbose=False): new_distance = self._distance() assert new_distance >= 0, 'Found non-positive distance %d' % new_distance - if verbose: - print('Distance in step %d: %g' % (step, new_distance)) + logger.info('Distance in step %d: %g' % (step, new_distance)) if new_distance == old_distance: converged = True - if verbose: - print('Exact minimum found after %d steps' % step) + logger.info('Exact minimum found after %d steps' % step) break rel_change = (old_distance - new_distance) / old_distance @@ -204,16 +204,15 @@ def run(self, eps=1e-4, kill=True, max_steps=50, verbose=False): if rel_change < eps and not converged and step > 0: converged = True - if verbose and new_distance != old_distance: - print('Close enough to local minimum after %d steps' % step) + if new_distance != old_distance: + logger.info('Close enough to local minimum after %d steps' % step) break # save distance for comparison in next step old_distance = new_distance self._cleanup(kill, verbose) - if verbose: - print('%d components remain.' % len(self.g.components)) + logger.info('%d components remain.' % len(self.g.components)) if converged: return step diff --git a/pypmc/mix_adapt/pmc.pyx b/pypmc/mix_adapt/pmc.pyx index 7451195..b6b542a 100644 --- a/pypmc/mix_adapt/pmc.pyx +++ b/pypmc/mix_adapt/pmc.pyx @@ -17,6 +17,8 @@ from pypmc.tools._linalg cimport bilinear_sym from libc.math cimport exp, log cimport numpy as _np +import logging +logger = logging.getLogger(__name__) cdef _np.ndarray[double, ndim=2] calculate_rho_rb(_np.ndarray[double, ndim=2] samples, density, live_components): @@ -111,7 +113,7 @@ def _prepare_pmc_update(_np.ndarray[double, ndim=2] samples, weights, latent, mi density.weights[k] = 0. # when a component is pruned, the other weights must be renormalized need_renormalize = True - print("Component %i died because of too few (%i) samples." %(k, count[k])) + logger.warning("Component %i died because of too few (%i) samples." %(k, count[k])) return density, rho, weight_normalization, live_components, need_renormalize @@ -232,7 +234,7 @@ def gaussian_pmc(_np.ndarray[double, ndim=2] samples not None, density, try: component.update(mu[k], cov[k]) except _np.linalg.LinAlgError: - print("Could not update component %i --> weight is set to zero." %k) + logger.warning("Could not update component %i --> weight is set to zero." %k) component.update(old_mu, old_sigma) density.weights[k] = 0. # when a component is pruned, the other weights must be renormalized @@ -440,17 +442,15 @@ class PMC(object): # recompute bound in 1st step else: old_bound = self.log_likelihood() - if verbose: - print('New bound=%g, K=%i' % (old_bound,len(self.density))) + logger.info('New bound=%g, K=%i' % (old_bound,len(self.density))) self.pmc(self.samples, self.density, self.weights, self.latent, self.rb, mincount=self.mincount, copy=False, **self.additional_args) bound = self.log_likelihood() - if verbose: - print('After update %d: bound=%.15g, K=%i, component_weights=%s' % (i, bound, len(self.density), self.density.weights)) + logger.info('After update %d: bound=%.15g, K=%i, component_weights=%s' % (i, bound, len(self.density), self.density.weights)) if bound < old_bound: - print('WARNING: bound decreased from %g to %g' % (old_bound, bound)) + logger.warning('Bound decreased from %g to %g' % (old_bound, bound)) # exact convergence if bound == old_bound: @@ -695,7 +695,7 @@ def student_t_pmc(_np.ndarray[double, ndim=2] samples not None, density, weights try: new_dof[k] = _find_root(nu_condition, mindof, maxdof, maxiter=dof_solver_steps) except RuntimeError: # occurs if not converged - print("WARNING: ``dof`` solver for component %i did not converge." % k) + logger.warning("``dof`` solver for component %i did not converge." % k) new_dof[k] = density.components[k].dof except ValueError as error: # check if nu_condition has the same sign at mindof and maxdof @@ -727,7 +727,7 @@ def student_t_pmc(_np.ndarray[double, ndim=2] samples not None, density, weights try: component.update(mu[k], cov[k], new_dof[k]) except _np.linalg.LinAlgError: - print("Could not update component %i --> weight is set to zero." % k) + logger.warning("Could not update component %i --> weight is set to zero." % k) component.update(old_mu, old_sigma, old_dof) density.weights[k] = 0. # when a component is pruned, the other weights must be renormalized diff --git a/pypmc/mix_adapt/variational.pyx b/pypmc/mix_adapt/variational.pyx index f72b86c..4824770 100644 --- a/pypmc/mix_adapt/variational.pyx +++ b/pypmc/mix_adapt/variational.pyx @@ -18,6 +18,9 @@ cimport numpy as _np from libc.math cimport exp, log from pypmc.tools._linalg cimport bilinear_sym, chol_inv_det +import logging +logger = logging.getLogger(__name__) + DTYPE = _np.float64 ctypedef double DTYPE_t @@ -161,13 +164,13 @@ class GaussianInference(object): # because in that case \frac{\alpha_k - 1}{normalization} > 0 pi = self.alpha[k] - 1. if pi <= 0: - print("Skipped component %i because of zero weight" %k) + logger.warning("Skipped component %i because of zero weight" %k) skipped.append(k) continue # Gauss-Wishart mode if self.nu[k] <= self.dim: - print("WARNING: Gauss-Wishart mode of component %i is not defined" %k) + logger.warning("Gauss-Wishart mode of component %i is not defined" %k) skipped.append(k) continue @@ -176,8 +179,7 @@ class GaussianInference(object): cov = chol_inv_det(W)[1] components.append(Gauss(self.m[k], cov)) except Exception as error: - print("ERROR: Could not create component %i." %k) - print("The error was:", repr(error) ) + logger.error("Could not create component %i. The error was: %s" %(k, repr(error))) skipped.append(k) continue @@ -185,7 +187,7 @@ class GaussianInference(object): weights.append(pi) if skipped: - print("The following components have been skipped:", skipped) + logger.warning("The following components have been skipped:", skipped) return MixtureDensity(components, weights) @@ -324,17 +326,15 @@ class GaussianInference(object): old_bound = bound else: old_bound = self.likelihood_bound() - if verbose: - print('New bound=%g, K=%d, N_k=%s' % (old_bound, self.K, self.N_comp)) + logger.info('New bound=%g, K=%d, N_k=%s' % (old_bound, self.K, self.N_comp)) self.update() bound = self.likelihood_bound() - if verbose: - print('After update %d: bound=%.15g, K=%d, N_k=%s' % (i, bound, self.K, self.N_comp)) + logger.info('After update %d: bound=%.15g, K=%d, N_k=%s' % (i, bound, self.K, self.N_comp)) if bound < old_bound: - print('WARNING: bound decreased from %g to %g' % (old_bound, bound)) + logger.warning('Bound decreased from %g to %g' % (old_bound, bound)) # exact convergence if bound == old_bound: diff --git a/pypmc/sampler/markov_chain.py b/pypmc/sampler/markov_chain.py index 33ac444..d59a74f 100644 --- a/pypmc/sampler/markov_chain.py +++ b/pypmc/sampler/markov_chain.py @@ -6,6 +6,9 @@ from ..tools.indicator import merge_function_with_indicator as _indmerge from ..tools._doc import _inherit_docstring +import logging +logger = logging.getLogger(__name__) + class MarkovChain(object): r"""A Markov chain to generate samples from the target density. @@ -375,15 +378,15 @@ def adapt(self): try: self.proposal.update(scaled_sigma) except _np.linalg.LinAlgError: - print("WARNING: Markov chain self adaptation failed; trying diagonalization ... ", end='') + logger.warning("Markov chain self adaptation failed; trying diagonalization") # try to insert offdiagonal elements only diagonal_matrix = _np.zeros_like(scaled_sigma) _np.fill_diagonal(diagonal_matrix, _np.diag(scaled_sigma)) try: self.proposal.update(diagonal_matrix) - print('success') + logger.warning('Diagonalization succeeded') except _np.linalg.LinAlgError: - print('fail') + logger.warning('Diagonalization failed') # just scale the old covariance matrix if everything else fails self.proposal.update(self.proposal.sigma / self.covar_scale_multiplier) diff --git a/pypmc/tools/_partition.py b/pypmc/tools/_partition.py index a4a3fb3..a70e22a 100644 --- a/pypmc/tools/_partition.py +++ b/pypmc/tools/_partition.py @@ -6,6 +6,9 @@ from ..density.gauss import Gauss from ..density.mixture import MixtureDensity +import logging +logger = logging.getLogger(__name__) + def partition(N, k): '''Distribute ``N`` into ``k`` parts such that each part takes the value ``N//k`` or ``N//k + 1`` where ``//`` denotes integer @@ -65,25 +68,22 @@ def patch_data(data, L=100, try_diag=True, verbose=False): this_comp = Gauss(mean, cov) components.append(this_comp) except _np.linalg.LinAlgError as error1: - if verbose: - print("Could not form Gauss from patch %i. Reason: %s" % (i, repr(error1))) + logger.info("Could not form Gauss from patch %i. Reason: %s" % (i, repr(error1))) if try_diag: cov = _np.diag(_np.diag(cov)) try: this_comp = Gauss(mean, cov) components.append(this_comp) - if verbose: - print('Diagonal covariance attempt succeeded.') + logger.info('Diagonal covariance attempt succeeded.') except _np.linalg.LinAlgError as error2: skipped.append(i) - if verbose: - print("Diagonal covariance attempt failed. Reason: %s" % repr(error2)) + logger.info("Diagonal covariance attempt failed. Reason: %s" % repr(error2)) else: # if not try_diag skipped.append(i) # print skipped components if any if skipped: - print("WARNING: Could not form Gaussians from: %s" % skipped) + logger.warning("Could not form Gaussians from: %s" % skipped) # create and return mixture return MixtureDensity(components)