From 46d9867995956dab1dfad5d3ab566bbb45a7ceec Mon Sep 17 00:00:00 2001 From: Retief Lubbe Date: Fri, 24 Nov 2023 23:19:03 +0100 Subject: [PATCH] Remove class attribures. Related to #55 --- grainlearning/dynamic_systems.py | 1 + grainlearning/inference.py | 44 ++++++----- grainlearning/iterative_bayesian_filter.py | 65 ++++++++-------- grainlearning/sampling.py | 89 ++++++++++------------ 4 files changed, 96 insertions(+), 103 deletions(-) diff --git a/grainlearning/dynamic_systems.py b/grainlearning/dynamic_systems.py index d0996cb..bc77997 100644 --- a/grainlearning/dynamic_systems.py +++ b/grainlearning/dynamic_systems.py @@ -111,6 +111,7 @@ class DynamicSystem: :param estimated_params_cv: Estimated parameter coefficient of variation as the second moment of the distribution (:math:`x_\sigma = \sqrt{\sum_i w_i * (x_i - x_\mu)^2} / x_\mu`), defaults to None, optional """ + def __init__( self, obs_data: np.ndarray, diff --git a/grainlearning/inference.py b/grainlearning/inference.py index 18e9ccd..815ec99 100644 --- a/grainlearning/inference.py +++ b/grainlearning/inference.py @@ -2,9 +2,10 @@ This module contains various methods for performing statistical and Bayesian inference """ from typing import Type + import numpy as np -from scipy.stats import multivariate_normal from grainlearning.dynamic_systems import DynamicSystem +from scipy.stats import multivariate_normal class SMC: @@ -43,39 +44,40 @@ class SMC: with the maxima of the observations, defaults to True :param cov_matrices: Covariance matrices of shape (num_steps, num_obs, num_obs), defaults to None, Optional + :param likelihoods: Likelihood distributions of shape (num_steps, num_samples) + :param posteriors: Posterior distributions of shape (num_steps, num_samples) + :param ess: Time evolution of the effective sample size """ - #: Target effective sample size - ess_target: float - - #: True if the covariance matrix is scaled with the maximum of the observations, defaults to True - scale_cov_with_max: bool = True - - #: Covariance matrices of shape (num_steps, num_obs, num_obs) - cov_matrices: np.array - - #: Likelihood distributions of shape (num_steps, num_samples) - likelihoods: np.array - - #: Posterior distributions of shape (num_steps, num_samples) - posteriors: np.array - - #: Time evolution of the effective sample size - ess: np.array - def __init__( self, ess_target: float, scale_cov_with_max: bool = True, cov_matrices: np.array = None, ): - """Initialize the SMC class""" + """Initialize the SMC class + + Parameters + ---------- + ess_target : float + Target effective sample size + scale_cov_with_max : bool, optional + True if the covariance matrix is scaled with the maximum of the observations, by default True + cov_matrices : np.array, optional + Covariance matrices of shape (num_steps, num_obs, num_obs), by default None + """ self.ess_target = ess_target self.scale_cov_with_max = scale_cov_with_max self.cov_matrices = cov_matrices + self.likelihoods = None + + self.posteriors = None + + self.ess = None + @classmethod def from_dict(cls: Type["SMC"], obj: dict): """Initialize the class using a dictionary style @@ -197,4 +199,4 @@ def compute_effective_sample_size(self): ess = 1.0 / np.sum(self.posteriors ** 2, axis=1) ess /= num_samples ess = ess.reshape(num_steps, 1) - self.ess = ess + self.ess = ess \ No newline at end of file diff --git a/grainlearning/iterative_bayesian_filter.py b/grainlearning/iterative_bayesian_filter.py index d424c71..04a7e2f 100644 --- a/grainlearning/iterative_bayesian_filter.py +++ b/grainlearning/iterative_bayesian_filter.py @@ -2,14 +2,15 @@ This module contains various Bayesian filtering classes by mixing the inference and sampling methods from the :mod:`.inference` and :mod:`.sampling` modules. """ -from typing import Type, List from pickle import load +from typing import List, Type + import numpy as np -from scipy import optimize from grainlearning.dynamic_systems import DynamicSystem, IODynamicSystem from grainlearning.inference import SMC from grainlearning.sampling import GaussianMixtureModel, generate_params_qmc from grainlearning.tools import voronoi_vols +from scipy import optimize class IterativeBayesianFilter: @@ -73,45 +74,37 @@ class IterativeBayesianFilter: :param ess_tol: Tolerance for the target effective sample size to converge, defaults to 1.0e-2 :param proposal: A proposal distribution to sample the state-parameter space, defaults to None :param proposal_data_file: Pickle that stores the previously trained proposal distribution, defaults to None + :param param_data_list: List of the parameter samples of shape (num_samples, num_params) generated in all iterations + :param sigma_list: List of sigma values optimized to satisfy the target effective sample size in all iterations + :param posterior: The posterior distribution of model states at the last time step """ - #: The inference class quantify the evolution of the posterior distribution of model states over time - inference = Type["SMC"] - - #: The sampling class generates new samples from the proposal density - sampling = Type["GaussianMixtureModel"] - - #: List of the parameter samples of shape (num_samples, num_params) generated in all iterations - param_data_list: List = [] - - #: List of sigma values optimized to satisfy the target effective sample size in all iterations - sigma_list: List = [] - - #: This a tolerance to which the optimization algorithm converges. - ess_tol: float = 1.0e-2 - - #: The non-informative distribution to draw the initial samples - initial_sampling: str = "halton" - - #: The current proposal distribution - proposal: np.ndarray = None - - #: The next proposal distribution - posterior: np.ndarray = None - - #: The name of the file that stores the current proposal distribution - proposal_data_file: str = None - def __init__( self, - inference: Type["SMC"], - sampling: Type["GaussianMixtureModel"], + inference: Type["SMC"] = None, + sampling: Type["GaussianMixtureModel"] = None, ess_tol: float = 1.0e-2, initial_sampling: str = 'halton', proposal: np.ndarray = None, proposal_data_file: str = None, ): - """Initialize the Iterative Bayesian Filter.""" + """Initialize the Iterative Bayesian Filter class + + Parameters + ---------- + inference : Type["SMC"], optional + The inference class quantify the evolution of the posterior distribution of model states over time, by default None + sampling : Type["GaussianMixtureModel"], optional + The sampling class generates new samples from the proposal density, by default None + ess_tol : float, optional + This a tolerance to which the optimization algorithm converges, by default 1.0e-2 + initial_sampling : str, optional + The non-informative distribution to draw the initial samples, by default 'halton' + proposal : np.ndarray, optional + The current proposal distribution, by default None + proposal_data_file : str, optional + _description_, by default None + """ self.inference = inference @@ -125,6 +118,12 @@ def __init__( self.proposal_data_file = proposal_data_file + self.param_data_list = [] + + self.sigma_list = [] + + self.posterior = None + @classmethod def from_dict(cls: Type["IterativeBayesianFilter"], obj: dict): """Initialize the class using a dictionary style @@ -238,4 +237,4 @@ def save_proposal_to_file(self, system: Type["IODynamicSystem"]): :param system: Dynamic system class """ - self.sampling.save_gmm_to_file(f'{system.sim_data_dir}/iter{system.curr_iter-1}/{self.proposal_data_file}') + self.sampling.save_gmm_to_file(f'{system.sim_data_dir}/iter{system.curr_iter-1}/{self.proposal_data_file}') \ No newline at end of file diff --git a/grainlearning/sampling.py b/grainlearning/sampling.py index ddc4d21..5a87b8a 100644 --- a/grainlearning/sampling.py +++ b/grainlearning/sampling.py @@ -1,13 +1,13 @@ """ This module contains various methods to sample the state-parameter space of a dynamic system. """ -from typing import Type from pickle import dump, load +from typing import Type + import numpy as np -from sklearn.mixture import BayesianGaussianMixture -from scipy.stats.qmc import Sobol, Halton, LatinHypercube from grainlearning.dynamic_systems import DynamicSystem - +from scipy.stats.qmc import Halton, LatinHypercube, Sobol +from sklearn.mixture import BayesianGaussianMixture # from grainlearning.tools import regenerate_params_with_gmm, unweighted_resample# @@ -85,53 +85,18 @@ class GaussianMixtureModel: This can speed up convergence when fit is called several times on similar problems. See the Glossary. :param expand_factor: factor used when converting the ensemble from weighted to unweighted, defaults to 10, optional :param slice_sampling: flag to use slice sampling, defaults to False, optional + :param gmm: The class of the Gaussian Mixture Model + :param max_params: Current maximum values of the parameters """ - #: Maximum number of components - max_num_components: int = 0 - - #: The dirichlet concentration of each component on the weight distribution (Dirichlet), default to None. - weight_concentration_prior: float = 0.0 - - #: String describing the type of covariance parameters to use. - covariance_type: str = "full" - - #: number of initialization to perform, defaults to 1. - n_init: int = 1 - - #: tolerance threshold, defaults to 1.0e-3. - tol: float = 1.0e-3 - - #: maximum number of EM iterations to perform, defaults to 100 - max_iter: int = 100 - - #: random seed given to the method chosen to initialize the weights, the means and the covariances. - random_state: int - - #: The method used to initialize the weights, the means and the covariances. - init_params: str = "kmeans" - - #: flag to use warm start, defaults to False. - warm_start: bool = False - - #: the factor used when converting and populating the ensemble from weighted to unweighted, defaults to 10. - expand_factor: int = 10 - - #: flag to use slice sampling, defaults to False. - slice_sampling: False - - #: The class of the Gaussian Mixture Model - gmm: Type["BayesianGaussianMixture"] - - #: Current maximum values of the parameters - max_params = None + def __init__( self, - max_num_components, - weight_concentration_prior: float = None, - covariance_type: str = "tied", + max_num_components=0, + weight_concentration_prior: float = 0.2, + covariance_type: str = "full", n_init: int = 1, - tol: float = 1.0e-5, + tol: float = 1.0e-3, max_iter: int = 100, random_state: int = None, init_params: str = "kmeans", @@ -139,7 +104,33 @@ def __init__( expand_factor: int = 10, slice_sampling: bool = False, ): - """ Initialize the Gaussian Mixture Model class""" + """Initialize the Gaussian mixture model. + + Parameters + ---------- + max_num_components : _type_ + Maximum number of components + weight_concentration_prior : float, optional + The dirichlet concentration of each component on the weight distribution (Dirichlet), by default None + covariance_type : str, optional + String describing the type of covariance parameters to use, by default "tied" + n_init : int, optional + number of initialization to perform, by default 1 + tol : float, optional + Tolerance threshold, defaults to 1.0e-3, by default 1.0e-5 + max_iter : int, optional + Maximum number of EM iterations to perform, by default 100 + random_state : int, optional + Random seed given to the method chosen to initialize the weights, the means and the covariances, by default None + init_params : str, optional + The method used to initialize the weights, the means and the covariances, by default "kmeans" + warm_start : bool, optional + Flag to use warm start, defaults to False., by default False + expand_factor : int, optional + The factor used when converting and populating the ensemble from weighted to unweighted, by default 10 + slice_sampling : bool, optional + Flag to use slice sampling, by default False + """ self.max_num_components = max_num_components if weight_concentration_prior is None: @@ -169,7 +160,7 @@ def __init__( self.expanded_normalized_params = None - self.gmm = Type["BayesianGaussianMixture"] + self.gmm = None @classmethod def from_dict(cls: Type["GaussianMixtureModel"], obj: dict): @@ -370,4 +361,4 @@ def generate_params_qmc(system: Type["DynamicSystem"], num_samples: int, method: mean + (param_table[sim_i][param_i] - 0.5) * 2 * std ) - return np.array(param_table, ndmin=2) + return np.array(param_table, ndmin=2) \ No newline at end of file