diff --git a/.github/workflows/code_style.yml b/.github/workflows/code_style.yml index e7269be5..4dd23527 100644 --- a/.github/workflows/code_style.yml +++ b/.github/workflows/code_style.yml @@ -3,7 +3,7 @@ name: Python style on: pull_request: - # types: [opened] + types: [opened] jobs: qa: name: Quality check diff --git a/.gitignore b/.gitignore index aaf33e3e..46b8e870 100644 --- a/.gitignore +++ b/.gitignore @@ -23,5 +23,6 @@ __pycache__ build docs/build docs/source/_build +docs/source/build debug.py -docs/source/reference/* +docs/source/reference/release_notes.rst diff --git a/alea/examples/gaussian_model.py b/alea/examples/gaussian_model.py index 86f19b0e..6061bff9 100644 --- a/alea/examples/gaussian_model.py +++ b/alea/examples/gaussian_model.py @@ -1,32 +1,62 @@ -from typing import Optional +from typing import Dict, List, Optional -from scipy import stats import numpy as np +from scipy import stats from alea.model import StatisticalModel class GaussianModel(StatisticalModel): + """ + A model of a gaussian measurement, where the model has parameters mu and sigma. + For illustration, we show how required nominal parameters can be added + to the init sigma is fixed in this example. + + Args: + parameter_definition (dict or list, optional (default=None)): + definition of the parameters of the model + + Caution: + You must define the nominal values of the parameters (mu, sigma) + in the parameters definition. + """ + def __init__( self, - parameter_definition: Optional[dict or list] = None, + parameter_definition: Optional[Dict or List] = None, **kwargs, ): - """ - Initialise a model of a gaussian measurement (hatmu), - where the model has parameters mu and sigma - For illustration, we show how required nominal parameters can be added to the init - sigma is fixed in this example. - """ + """Initialise a gaussian model.""" if parameter_definition is None: parameter_definition = ["mu", "sigma"] super().__init__(parameter_definition=parameter_definition, **kwargs) def _ll(self, mu=None, sigma=None): + """ + Log-likelihood of the model. + + Args: + mu (float, optional (default=None)): mean of the gaussian, + if None, the nominal value is used + sigma (float, optional (default=None)): standard deviation of the gaussian, + if None, the nominal value is used + """ hat_mu = self.data[0]['hat_mu'][0] return stats.norm.logpdf(x=hat_mu, loc=mu, scale=sigma) def _generate_data(self, mu=None, sigma=None): + """ + Generate data from the model. + + Args: + mu (float, optional (default=None)): mean of the gaussian, + if None, the nominal value is used + sigma (float, optional (default=None)): standard deviation of the gaussian, + if None, the nominal value is used + + Returns: + list: data generated from the model + """ hat_mu = stats.norm(loc=mu, scale=sigma).rvs() data = [np.array([(hat_mu,)], dtype=[('hat_mu', float)])] return data diff --git a/alea/model.py b/alea/model.py index 51c57510..742cab37 100644 --- a/alea/model.py +++ b/alea/model.py @@ -1,12 +1,11 @@ import inspect import warnings -from typing import Tuple, Callable, Optional +from typing import Dict, List, Tuple, Callable, Optional import numpy as np from scipy.stats import chi2 from scipy.optimize import brentq from iminuit import Minuit -from iminuit.util import make_func_code from blueice.likelihood import _needs_data from inference_interface import toydata_to_file @@ -16,39 +15,53 @@ class StatisticalModel: """ Class that defines a statistical model. - The statisical model contains two parts that you must define yourself: - - a likelihood function, ll(self, parameter_1, parameter_2... parameter_n): - a function of a set of named parameters - returns a float expressing the loglikelihood for observed data - given these parameters - - a data generation method generate_data(self, parameter_1, parameter_2... parameter_n): - a function of the same set of named parameters - returns a full data set: - - Methods: - __init__ - required to implement: - - _ll - _generate_data - - optional to implement: - get_expectation_values - - Implemented here: - store_data - fit - get_parameter_list - - Other members: - _data = None - _config = {} - _confidence_level = 0.9 - _confidence_interval_kind = "upper, lower, central" - (if your threshold is the FC threshold, "central" gives you the unified interval) - _confidence_interval_threshold: function that defines the Neyman threshold for limit calculations - _fit_guess = {} - _fixed_parameters = [] + + - The statisical model contains two parts that you must define yourself: + - a likelihood function + ll(self, parameter_1, parameter_2... parameter_n): + A function of a set of named parameters which + return a float expressing the loglikelihood for observed data given these parameters. + - a data generation function + generate_data(self, parameter_1, parameter_2... parameter_n): + A function of the same set of named parameters return a full data set. + - Methods that you must implement: + - _ll + - _generate_data + - Methods that you may implement: + - get_expectation_values + - Methods that already exist here: + - ll + - store_data + - fit + - get_parameter_list + - confidence_interval + + The public methods generate_data and ll, as the names suggested, + depend on private methods _generate_data, and _ll respectively. + + Attributes: + data: data of the model + _data: data of the model + _confidence_level: confidence level for confidence intervals + _confidence_interval_kind: kind of confidence interval to compute + parameters: parameters of the model + confidence_interval_threshold: threshold for confidence interval + is_data_set (bool): True if data is set + + Args: + data: pre-set data of the model + parameter_definition (dict or list, optional (default=None)): + definition of the parameters of the model + confidence_level (float, optional (default=0.9)): + confidence level for confidence intervals + confidence_interval_kind (str, optional (default="central")): + kind of confidence interval to compute + confidence_interval_threshold (Callable[[float], float], optional (default=None)): + threshold for confidence interval + + Raise: + RuntimeError: if you try to instantiate the StatisticalModel class directly + NotImplementedError: if you do not implement the likelihood function or the data generation """ def __init__( @@ -72,6 +85,8 @@ def __init__( if data is not None: self.data = data self._confidence_level = confidence_level + if confidence_interval_kind not in {"central", "upper", "lower"}: + raise ValueError("confidence_interval_kind must be one of central, upper, lower") self._confidence_interval_kind = confidence_interval_kind self.confidence_interval_threshold = confidence_interval_threshold self._define_parameters(parameter_definition) @@ -79,6 +94,7 @@ def __init__( self._check_ll_and_generate_data_signature() def _define_parameters(self, parameter_definition): + """Initialize the parameters of the model""" if parameter_definition is None: self.parameters = Parameters() elif isinstance(parameter_definition, dict): @@ -89,6 +105,7 @@ def _define_parameters(self, parameter_definition): raise RuntimeError("parameter_definition must be dict or list") def _check_ll_and_generate_data_signature(self): + """Check that the likelihood and generate_data functions have the same signature""" ll_params = set(inspect.signature(self._ll).parameters) generate_data_params = set(inspect.signature(self._generate_data).parameters) if ll_params != generate_data_params: @@ -96,11 +113,13 @@ def _check_ll_and_generate_data_signature(self): "ll and generate_data must have the same signature (parameters)") def _ll(self, **kwargs) -> float: + """Likelihood function, return the loglikelihood for the given parameters.""" raise NotImplementedError( "You must write a likelihood function (_ll) for your statistical model" " or use a subclass where it is written for you") def _generate_data(self, **kwargs): + """Generate data for the given parameters.""" raise NotImplementedError( "You must write a data-generation method (_generate_data) for your statistical model" " or use a subclass where it is written for you") @@ -112,13 +131,16 @@ def ll(self, **kwargs) -> float: The parameters are passed as keyword arguments, positional arguments are not possible. If a parameter is not given, the default value is used. + Keyword Args: + kwargs: keyword arguments for the parameters + Returns: - float: Likelihood value + float: likelihood value """ parameters = self.parameters(**kwargs) return self._ll(**parameters) - def generate_data(self, **kwargs): + def generate_data(self, **kwargs) -> dict or list: """ Generate data for the given parameters. The parameters are passed as keyword arguments, positional arguments are not possible. @@ -128,10 +150,11 @@ def generate_data(self, **kwargs): ValueError: If the parameters are not within the fit limits Returns: - Data + dict or list: generated data + + Caution: + This implementation won't allow you to call generate_data by positional arguments. """ - # CAUTION: - # This implementation won't allow you to call generate_data by positional arguments. if not self.parameters.values_in_fit_limits(**kwargs): raise ValueError("Values are not within fit limits") generate_values = self.parameters(**kwargs) @@ -150,23 +173,30 @@ def data(self): @data.setter def data(self, data): - """ - Simple setter for a data-set-- mainly here so it can be over-ridden for special needs. - Data-sets are expected to be in the form of a list of one or more structured arrays, - representing the data-sets of one or more likelihood terms. - """ + """data setter""" self._data = data self.is_data_set = True def store_data( - self, file_name, data_list, data_name_list=None, metadata = None): + self, + file_name, data_list, + data_name_list: Optional[List] = None, + metadata: Optional[Dict] = None): """ Store a list of datasets (each on the form of a list of one or more structured arrays) Using inference_interface, but included here to allow over-writing. - structure would be: [[datasets1], [datasets2], ..., [datasetsn]] - where each of datasets is a list of structured arrays - if you specify, it is set, if not it will read from self.get_likelihood_term_names - if not defined, it will be ["0", "1", ..., "n-1"] + The structure would be: [[datasets1], [datasets2], ..., [datasetsn]], + where each of datasets is a list of structured arrays. + If you specify, it is set, if not it will read from self.get_likelihood_term_names. + If not defined, it will be ["0", "1", ..., "n-1"]. The metadata is optional. + + Args: + file_name (str): name of the file to store the data in + data_list (list): list of datasets + data_name_list (list, optional (default=None)): list of names of the datasets. + If None, it will be read from self.get_likelihood_term_names + metadata (dict, optional (default=None)): metadata to store with the data. + If None, no metadata is stored. """ if data_name_list is None: if hasattr(self, "likelihood_names"): @@ -178,6 +208,12 @@ def store_data( toydata_to_file(file_name, data_list, data_name_list, **kw) def get_expectation_values(self, **parameter_values): + """ + Get the expectation values of the measurement. + + Args: + parameter_values: values of the parameters + """ return NotImplementedError("get_expectation_values is optional to implement") @property @@ -189,44 +225,57 @@ def nominal_expectation_values(self): """ return self.get_expectation_values() # no kwargs for nominal - def get_likelihood_term_from_name(self, likelihood_name): + def get_likelihood_term_from_name(self, likelihood_name: str) -> int: """ - Returns the index of a likelihood term if the likelihood has several names + Return the index of a likelihood term if the likelihood has several names + + Args: + likelihood_name (str): name of the likelihood term + + Returns: + int: index of the likelihood term """ if hasattr(self, "likelihood_names"): likelihood_names = self.likelihood_names - return {n:i for i,n in enumerate(likelihood_names)}[likelihood_name] + return {n: i for i, n in enumerate(likelihood_names)}[likelihood_name] else: raise NotImplementedError("The attribute likelihood_names is not defined.") def get_parameter_list(self): - """ - Returns a set of all parameters that the generate_data and likelihood accepts - """ + """Return a set of all parameters that the generate_data and likelihood accepts""" return self.parameters.names - def make_objective(self, minus=True, **kwargs): - sign = -1 if minus else 1 + def make_objective(self): + """ + Make a function that can be passed to Minuit + Returns: + Callable: function that can be passed to Minuit + """ def cost(args): - # Get the arguments from args, - # then fill in the ones already fixed in outer kwargs + # Get the arguments from args call_kwargs = {} for i, k in enumerate(self.parameters.names): call_kwargs[k] = args[i] - # call_kwargs.update(kwargs) - return self.ll(**call_kwargs) * sign + return self.ll(**call_kwargs) * -1 return cost @_needs_data def fit(self, verbose=False, **kwargs) -> Tuple[dict, float]: """ - Fit the model to the data by maximizing the likelihood - returns a dict containing best-fit values of each parameter, + Fit the model to the data by maximizing the likelihood. + Return a dict containing best-fit values of each parameter, and the value of the likelihood evaluated there. While the optimization is a minimization, - the likelihood returned is the _maximum_ of the likelihood. + the likelihood returned is the __maximum__ of the likelihood. + + Args: + verbose (bool): if True, print the Minuit object + + Returns: + dict, float: best-fit values of each parameter, + and the value of the likelihood evaluated there """ fixed_parameters = list(kwargs.keys()) guesses = self.parameters.fit_guesses @@ -235,11 +284,10 @@ def fit(self, verbose=False, **kwargs) -> Tuple[dict, float]: raise ValueError("Initial guesses are not within fit limits") defaults = self.parameters(**guesses) - cost = self.make_objective(minus=True, **kwargs) + cost = self.make_objective() # Make the Minuit object - m = Minuit(MinuitWrap(cost, parameters=self.parameters), - **defaults) + m = Minuit(MinuitWrap(cost, parameters=self.parameters), **defaults) m.errordef = Minuit.LIKELIHOOD fixed_params = [] if fixed_parameters is None else fixed_parameters fixed_params += self.parameters.not_fittable @@ -261,18 +309,32 @@ def _confidence_interval_checks( confidence_interval_kind: str, **kwargs): """ - helper function for confidence_interval that does the input checks and returns bounds + + Helper function for confidence_interval that does the input checks and return bounds + + Args: + poi_name (str): name of the parameter of interest + parameter_interval_bounds (Tuple[float, float]): range in which to search for the + confidence interval edges + confidence_level (float): confidence level for confidence intervals + confidence_interval_kind (str): kind of confidence interval to compute + + Returns: + Tuple[str, Callable[[float], float], Tuple[float, float]]: + confidence interval kind, confidence interval threshold, parameter interval bounds """ if confidence_level is None: confidence_level = self._confidence_level if confidence_interval_kind is None: confidence_interval_kind = self._confidence_interval_kind - mask = (confidence_level > 0) and (confidence_level < 1) - assert mask, "the confidence level must lie between 0 and 1" + if (confidence_level < 0) or (confidence_level > 1): + raise ValueError("confidence_level must be between 0 and 1") + parameter_of_interest = self.parameters[poi_name] - assert parameter_of_interest.fittable, "The parameter of interest must be fittable" - assert poi_name not in kwargs, "you cannot set the parameter you're constraining" + if not parameter_of_interest.fittable: + raise ValueError("The parameter of interest must be fittable") + if poi_name in kwargs: + raise ValueError("You cannot set the parameter you're constraining") if parameter_interval_bounds is None: parameter_interval_bounds = parameter_of_interest.parameter_interval_bounds @@ -318,19 +380,29 @@ def confidence_interval( confidence_interval_kind: str = None, **kwargs) -> Tuple[float, float]: """ - Uses self.fit to compute confidence intervals for a certain named parameter - - poi_name: string, name of fittable parameter of the model - parameter_interval_bounds: range in which to search for the confidence interval edges. - May be specified as: - - setting the property "parameter_interval_bounds" for the parameter - - passing a list here + Uses self.fit to compute confidence intervals for a certain named parameter. If the parameter is a rate parameter, and the model has expectation values implemented, - the bounds will be interpreted as bounds on the expectation value - (so that the range in the fit is parameter_interval_bounds/mus) - otherwise the bound is taken as-is. + the bounds will be interpreted as bounds on the expectation value, + so that the range in the fit is parameter_interval_bounds/mus. + Otherwise the bound is taken as-is. + + Args: + poi_name (str): name of the parameter of interest + parameter_interval_bounds (Tuple[float, float], optional (default=None)): range + in which to search for the confidence interval edges. May be specified as: + - setting the property "parameter_interval_bounds" for the parameter + - passing a list here + - passing None here, in which case the parameter_interval_bounds property of the parameter is used + confidence_level (float, optional (default=None)): + confidence level for confidence intervals. + If None, the default confidence level of the model is used. + confidence_interval_kind (str, optional (default=None)): + kind of confidence interval to compute. + If None, the default kind of the model is used. + + Keyword Args: + kwargs: the parameters for get_expectation_values and fit """ - ci_objects = self._confidence_interval_checks( poi_name, parameter_interval_bounds, @@ -381,9 +453,19 @@ class MinuitWrap: """ Wrapper for functions to be called by Minuit. Initialized with a function f and a Parameters instance. + + Attributes: + func: function wrapped + s_args (list): parameter names of the model + _parameters (dict): parameters and limits of the model + + Args: + f (Callable): function to be wrapped + parameters (Parameters): parameters of the model """ - def __init__(self, f, parameters: Parameters): + def __init__(self, f: Callable, parameters: Parameters): + """Initialize the wrapper""" self.func = f self.s_args = parameters.names self._parameters = {p.name: p.fit_limits for p in parameters} diff --git a/alea/models/blueice_extended_model.py b/alea/models/blueice_extended_model.py index 560c8afa..f12a0bd3 100644 --- a/alea/models/blueice_extended_model.py +++ b/alea/models/blueice_extended_model.py @@ -21,27 +21,31 @@ class BlueiceExtendedModel(StatisticalModel): This class extends the `StatisticalModel` class and provides methods for generating data and computing likelihoods based on blueice. + Attributes: + parameters (Parameters): Parameters object containing the parameters of the model. + data (dict): Data of the statistical model. + is_data_set (bool): Whether data is set. + _likelihood (LogLikelihoodSum): A blueice LogLikelihoodSum instance. + likelihood_names (list): List of likelihood names. + data_generators (list): List of data generators for each likelihood term. + Args: parameter_definition (dict): A dictionary defining the model parameters. likelihood_config (dict): A dictionary defining the likelihood. + + Todo: + analysis_space could be inferred from the data + (assert that all sources have the same analysis_space) """ def __init__(self, parameter_definition: dict, likelihood_config: dict): - """Initializes the statistical model. - - Args: - parameter_definition (dict): A dictionary defining the model parameters. - likelihood_config (dict): A dictionary defining the likelihood. - """ + """Initializes the statistical model.""" super().__init__(parameter_definition=parameter_definition) self._likelihood = self._build_ll_from_config(likelihood_config) self.likelihood_names = [t["name"] for t in likelihood_config["likelihood_terms"]] self.likelihood_names.append("ancillary_likelihood") self.data_generators = self._build_data_generators() - # TODO analysis_space could be inferred from the data - # (assert that all sources have the same analysis space) - @classmethod def from_config(cls, config_file_path: str) -> "BlueiceExtendedModel": """Initializes the statistical model from a yaml config file. @@ -58,7 +62,7 @@ def from_config(cls, config_file_path: str) -> "BlueiceExtendedModel": @property def data(self) -> dict: - """Returns the data of the statistical model.""" + """Return the data of the statistical model.""" return super().data @data.setter @@ -66,14 +70,15 @@ def data(self, data: dict): """ Overrides default setter. Will also set the data of the blueice ll. Data-sets are expected to be in the form of a list of one - or more structured arrays-- representing the data-sets of one or more likelihood terms. + or more structured arrays representing the data-sets of one or more likelihood terms. """ # iterate through all likelihood terms and set the science data in the blueice ll - # last entry in data are the generate_values + # except the generate_values for i, (dataset_name, d) in enumerate(data.items()): if dataset_name != "generate_values": ll_term = self._likelihood.likelihood_list[i] - assert dataset_name == ll_term.pdf_base_config["name"], "Likelihood names do not match." + if dataset_name != ll_term.pdf_base_config["name"]: + raise ValueError("Likelihood names do not match.") ll_term.set_data(d) self._data = data @@ -83,11 +88,25 @@ def get_expectation_values(self, **kwargs) -> dict: """ Return total expectation values (summed over all likelihood terms with the same name) given a number of named parameters (kwargs) - TODO: Current implementation is not elegant. - It copied the llh and sets the data to the copied llh, - because the call of llh needs data to be set. - But data is not needed for the expectation values. - We should update this function in the future after we stop using blueice. + + Args: + kwargs: Named parameters + + Returns: + dict: Dictionary of expectation values + + Caution: + The function silently drops parameters it can't handle! + + Todo: + Current implementation is not elegant. + It copied the llh and sets the data to the copied llh, + because the call of llh needs data to be set. + But data is not needed for the expectation values. + We should update this function in the future after we stop using blueice. + + Make a self.likelihood_temrs dict with the likelihood names as keys and + the corresponding likelihood terms as values. """ generate_values = self.parameters(**kwargs) # kwarg or nominal value ret = dict() @@ -96,7 +115,6 @@ def get_expectation_values(self, **kwargs) -> dict: self_copy = deepcopy(self) self_copy.data = self_copy.generate_data() - # TODO: Make a self.likelihood_temrs dict with the likelihood names as keys and the corresponding likelihood terms as values. # ancillary likelihood does not contribute for ll_term, parameter_names in zip( self_copy._likelihood.likelihood_list[:-1], @@ -111,10 +129,13 @@ def get_expectation_values(self, **kwargs) -> dict: def _build_ll_from_config(self, likelihood_config: dict) -> "LogLikelihoodSum": """ - Iterate through all likelihood terms and build blueice ll + Iterate through all likelihood terms and build blueice likelihood instances. Args: likelihood_config (dict): A dictionary defining the likelihood. + + Returns: + LogLikelihoodSum: A blueice LogLikelihoodSum instance. """ lls = [] @@ -149,7 +170,7 @@ def _build_ll_from_config(self, likelihood_config: dict) -> "LogLikelihoodSum": ll = likelihood_object(blueice_config) for source in config["sources"]: - # Set rate parameters + # set rate parameters rate_parameters = [ p for p in source["parameters"] if self.parameters[p].ptype == "rate"] if len(rate_parameters) != 1: @@ -164,11 +185,11 @@ def _build_ll_from_config(self, likelihood_config: dict) -> "LogLikelihoodSum": "Only rate multipliers that end on _rate_multiplier" " are currently supported.") - # Set efficiency parameters + # set efficiency parameters if source.get("apply_efficiency", False): self._set_efficiency(source, ll) - # Set shape parameters + # set shape parameters shape_parameters = [ p for p in source["parameters"] if self.parameters[p].ptype == "shape"] for p in shape_parameters: @@ -180,7 +201,7 @@ def _build_ll_from_config(self, likelihood_config: dict) -> "LogLikelihoodSum": ll.prepare() lls.append(ll) - # Ancillary likelihood + # ancillary likelihood ll = CustomAncillaryLikelihood(self.parameters.with_uncertainty) lls.append(ll) @@ -188,8 +209,16 @@ def _build_ll_from_config(self, likelihood_config: dict) -> "LogLikelihoodSum": return LogLikelihoodSum(lls, likelihood_weights=likelihood_weights) def _build_data_generators(self) -> list: + """ + Build data generators for all likelihood terms. + + Returns: + list: List of data generators for each likelihood term. + + Todo: + Also implement data generator for ancillary ll term. + """ # last one is AncillaryLikelihood - # IDEA: Also implement data generator for ancillary ll term. return [ BlueiceDataGenerator(ll_term) for ll_term in self._likelihood.likelihood_list[:-1]] @@ -197,8 +226,17 @@ def _ll(self, **generate_values) -> float: return self._likelihood(**generate_values) def _generate_data(self, **generate_values) -> dict: - # generate_values are already filtered and filled by the nominal values\ - # through the generate_data method in the parent class + """ + Generate data for all likelihood terms and ancillary likelihood. + + Keyword Args: + generate_values (dict): A dictionary of parameter values. + + Returns: + dict: A dict of data-sets, + with key of the likelihood term name, "ancillary_likelihood" and "generate_values". + """ + # generate_values are already filtered and filled by the nominal values data = self._generate_science_data(**generate_values) ancillary_keys = self.parameters.with_uncertainty.names generate_values_anc = {k: v for k, v in generate_values.items() if k in ancillary_keys} @@ -208,11 +246,22 @@ def _generate_data(self, **generate_values) -> dict: return data def _generate_science_data(self, **generate_values) -> dict: - science_data = [gen.simulate(**generate_values) - for gen in self.data_generators] + """Generate data for all science data terms terms.""" + science_data = [ + gen.simulate(**generate_values) + for gen in self.data_generators] return dict(zip(self.likelihood_names, science_data)) def _generate_ancillary_measurements(self, **generate_values) -> dict: + """ + Generate data for the ancillary likelihood. + + Keyword Args: + generate_values (dict): A dictionary of parameter values. + + Returns: + numpy.array: A numpy structured array of ancillary measurements. + """ ancillary_measurements = {} anc_ll = self._likelihood.likelihood_list[-1] ancillary_generators = anc_ll._get_constraint_functions(**generate_values) @@ -229,7 +278,17 @@ def _generate_ancillary_measurements(self, **generate_values) -> dict: return dict_to_structured_array(ancillary_measurements) - def _set_efficiency(self, source, ll): + def _set_efficiency(self, source: dict, ll): + """ + Set the efficiency of a source in the blueice ll. + + Args: + source (dict): A dictionary defining the source. + ll (LogLikelihood): A blueice LogLikelihood instance. + + Raises: + ValueError: If the efficiency_name is not specified in the source. + """ if "efficiency_name" not in source: raise ValueError(f"Unspecified efficiency_name for source {source['name']:s}") efficiency_name = source["efficiency_name"] @@ -260,18 +319,13 @@ class CustomAncillaryLikelihood(LogAncillaryLikelihood): Custom ancillary likelihood that can be used to add constraint terms for parameters of the likelihood. - Args: - parameters (Parameters): Parameters object containing the - parameters to be constrained. + Attributes: + parameters (Parameters): Parameters object containing the parameters to be constrained. + constraint_functions (dict): Dict of constraint functions for all ancillary parameters. """ def __init__(self, parameters: Parameters): - """Initialize the CustomAncillaryLikelihood. - - Args: - parameters (Parameters): Parameters object containing the - parameters to be constrained. - """ + """Initialize the CustomAncillaryLikelihood.""" self.parameters = parameters # check that there are no None values in the uncertainties dict if set(self.parameters.uncertainties.keys()) != set(self.parameters.names): @@ -280,9 +334,10 @@ def __init__(self, parameters: Parameters): parameter_list = self.parameters.names self.constraint_functions = self._get_constraint_functions() - super().__init__(func=self.ancillary_likelihood_sum, - parameter_list=parameter_list, - config=self.parameters.nominal_values) + super().__init__( + func=self.ancillary_likelihood_sum, + parameter_list=parameter_list, + config=self.parameters.nominal_values) self.pdf_base_config["name"] = "ancillary_likelihood" @property @@ -290,6 +345,9 @@ def constraint_terms(self) -> dict: """ Dict of all constraint terms (logpdf of constraint functions) of the ancillary likelihood. + + Returns: + dict: Dict of all constraint terms function. """ return {name: func.logpdf for name, func in self.constraint_functions.items()} @@ -298,7 +356,7 @@ def set_data(self, d: np.array): Set the data of the ancillary likelihood (ancillary measurements). Args: - d (np.array): Data of ancillary measurements, stored as numpy array + d (numpy.array): Data of ancillary measurements, stored as numpy array. """ # This results in shifted constraint terms. d_dict = structured_array_to_dict(d) @@ -322,6 +380,18 @@ def ancillary_likelihood_sum(self, evaluate_at: dict) -> float: return np.sum(evaluated_constraint_terms) def _get_constraint_functions(self, **generate_values) -> dict: + """ + Get callable constraint functions for all ancillary parameters. + + Keyword Args: + generate_values (dict): A dictionary of parameter values. + + Returns: + dict: Dict of constraint functions for all ancillary parameters. + + Todo: + Implement str-type uncertainties. + """ central_values = self.parameters(**generate_values) constraint_functions = {} for name, uncertainty in self.parameters.uncertainties.items(): @@ -332,7 +402,6 @@ def _get_constraint_functions(self, **generate_values) -> dict: func = stats.norm( central_values[name], uncertainty) else: - # TODO: Implement str-type uncertainties NotImplementedError( "Only float uncertainties are supported at the moment.") constraint_functions[name] = func diff --git a/alea/parameters.py b/alea/parameters.py index e7f26da0..4825bcbc 100644 --- a/alea/parameters.py +++ b/alea/parameters.py @@ -7,19 +7,21 @@ class Parameter: Attributes: name (str): The name of the parameter. - nominal_value (float, optional): The nominal value of the parameter. - fittable (bool, optional): Indicates if the parameter is fittable or always fixed. - ptype (str, optional): The ptype of the parameter. - uncertainty (float or str, optional): - The uncertainty of the parameter. If a string, - it can be evaluated as a numpy or scipy function to define non-gaussian constraints. - relative_uncertainty (bool, optional): - Indicates if the uncertainty is relative to the nominal_value. - blueice_anchors (list, optional): Anchors for blueice template morphing. - fit_limits (tuple, optional): The limits for fitting the parameter. - parameter_interval_bounds (tupe, optional): limits for computing confidence intervals - fit_guess (float, optional): The initial guess for fitting the parameter. - description (str, optional): A description of the parameter. + nominal_value (float, optional (default=None)): The nominal value of the parameter. + fittable (bool, optional (default=None)): + Indicates if the parameter is fittable or always fixed. + ptype (str, optional (default=None)): The ptype of the parameter. + uncertainty (float or str, optional (default=None)): The uncertainty of the parameter. + If a string, it can be evaluated as a numpy or + scipy function to define non-gaussian constraints. + relative_uncertainty (bool, optional (default=None)): + Indicates if the uncertainty is relative to the nominal_value. + blueice_anchors (list, optional (default=None)): Anchors for blueice template morphing. + fit_limits (tuple, optional (default=None)): The limits for fitting the parameter. + parameter_interval_bounds (tuple, optional (default=None)): + Limits for computing confidence intervals + fit_guess (float, optional (default=None)): The initial guess for fitting the parameter. + description (str, optional (default=None)): A description of the parameter. """ def __init__( @@ -36,6 +38,7 @@ def __init__( fit_guess: Optional[float] = None, description: Optional[str] = None, ): + """Initialise a parameter.""" self.name = name self.nominal_value = nominal_value self.fittable = fittable @@ -59,7 +62,7 @@ def __repr__(self) -> str: @property def uncertainty(self) -> float or Any: """ - Returns the uncertainty of the parameter. + Return the uncertainty of the parameter. If the uncertainty is a string, it can be evaluated as a numpy or scipy function. """ if isinstance(self._uncertainty, str): @@ -79,6 +82,7 @@ def uncertainty(self, value: float or str) -> None: @property def fit_guess(self) -> float: + """Return the initial guess for fitting the parameter.""" # make sure to only return fit_guess if fittable if self._fit_guess is not None and not self.fittable: raise ValueError( @@ -91,14 +95,14 @@ def fit_guess(self, value: float) -> None: self._fit_guess = value def __eq__(self, other: object) -> bool: - """Returns True if all attributes are equal""" + """Return True if all attributes are equal""" if isinstance(other, Parameter): return all(getattr(self, k) == getattr(other, k) for k in self.__dict__) else: return False def value_in_fit_limits(self, value: float) -> bool: - """Returns True if value is within fit_limits""" + """Return True if value is within fit_limits""" if self.fit_limits is None: return True elif self.fit_limits[0] is None: @@ -114,14 +118,25 @@ class Parameters: Represents a collection of parameters. Attributes: - parameters (dict): A dictionary to store the parameters, with parameter name as key. + names (List[str]): A list of parameter names. + fit_guesses (Dict[str, float]): A dictionary of fit guesses. + fit_limits (Dict[str, float]): A dictionary of fit limits. + fittable (List[str]): A list of parameter names which are fittable. + not_fittable (List[str]): A list of parameter names which are not fittable. + uncertainties (Dict[str, float or Any]): A dictionary of parameter uncertainties. + with_uncertainty (Parameters): A Parameters object with parameters with + a not-NaN uncertainty. + nominal_values (Dict[str, float]): A dictionary of parameter nominal values. + parameters (Dict[str, Parameter]): A dictionary to store the parameters, + with parameter name as key. """ def __init__(self): + """Initialise a collection of parameters.""" self.parameters: Dict[str, Parameter] = {} def __iter__(self) -> iter: - """Returns an iterator over the parameters. Each iteration returns a Parameter object.""" + """Return an iterator over the parameters. Each iteration return a Parameter object.""" return iter(self.parameters.values()) @classmethod @@ -130,7 +145,7 @@ def from_config(cls, config: Dict[str, dict]): Creates a Parameters object from a configuration dictionary. Args: - config (dict): The configuration dictionary. + config (dict): A dictionary of parameter configurations. Returns: Parameters: The created Parameters object. @@ -148,7 +163,7 @@ def from_list(cls, names: List[str]): Everything else is set to default values. Args: - names (list): List of parameter names. + names (List[str]): List of parameter names. Returns: Parameters: The created Parameters object. @@ -171,6 +186,9 @@ def add_parameter(self, parameter: Parameter) -> None: Args: parameter (Parameter): The Parameter object to add. + + Raises: + ValueError: If the parameter name already exists. """ if parameter.name in self.names: raise ValueError(f"Parameter {parameter.name} already exists.") @@ -178,16 +196,12 @@ def add_parameter(self, parameter: Parameter) -> None: @property def names(self) -> List[str]: - """ - Returns a list of parameter names. - """ + """A list of parameter names.""" return list(self.parameters.keys()) @property def fit_guesses(self) -> Dict[str, float]: - """ - Returns a dictionary of fit guesses. - """ + """A dictionary of fit guesses.""" return { name: param.fit_guess for name, param in self.parameters.items() @@ -195,9 +209,7 @@ def fit_guesses(self) -> Dict[str, float]: @property def fit_limits(self) -> Dict[str, float]: - """ - Returns a dictionary of fit limits. - """ + """A dictionary of fit limits.""" return { name: param.fit_limits for name, param in self.parameters.items() @@ -205,22 +217,20 @@ def fit_limits(self) -> Dict[str, float]: @property def fittable(self) -> List[str]: - """ - Returns a list of parameter names which are fittable. - """ + """A list of parameter names which are fittable.""" return [name for name, param in self.parameters.items() if param.fittable] @property def not_fittable(self) -> List[str]: - """ - Returns a list of parameter names which are not fittable. - """ + """A list of parameter names which are not fittable.""" return [name for name, param in self.parameters.items() if not param.fittable] @property def uncertainties(self) -> dict: """ - return a dict of name:uncertainty for all parameters with a not-NaN uncertainty. + A dict of uncertainties for all parameters with a not-NaN uncertainty. + + Caution: this is not the same as the parameter.uncertainty property. """ return {k: i.uncertainty for k, i in self.parameters.items() if i.uncertainty is not None} @@ -238,25 +248,28 @@ def with_uncertainty(self) -> "Parameters": @property def nominal_values(self) -> dict: - """ - return a dict of name:nominal value for all applicable parameters - """ + """A dict of nominal values for all parameters with a nominal value.""" return { k: i.nominal_value for k, i in self.parameters.items() if i.nominal_value is not None} def __call__( - self, return_fittable: bool = False, - **kwargs: Any) -> Dict[str, float]: + self, return_fittable: Optional[bool] = False, + **kwargs: Optional[Dict]) -> Dict[str, float]: """ - Returns a dictionary of parameter values, optionally filtered + Return a dictionary of parameter values, optionally filtered to return only fittable parameters. Args: - return_fittable (bool, optional): + return_fittable (bool, optional (default=False)): Indicates if only fittable parameters should be returned. - **kwargs: Additional keyword arguments to override parameter values. + + Keyword Args: + kwargs (dict): Additional keyword arguments to override parameter values. + + Raises: + ValueError: If a parameter name is not found. Returns: dict: A dictionary of parameter values. @@ -286,11 +299,11 @@ def __getattr__(self, name: str) -> Parameter: Args: name (str): The name of the parameter. - Returns: - Parameter: The retrieved Parameter object. - Raises: AttributeError: If the attribute is not found. + + Returns: + Parameter: The retrieved Parameter object. """ try: return super().__getattribute__('parameters')[name] @@ -304,11 +317,11 @@ def __getitem__(self, name: str) -> Parameter: Args: name (str): The name of the parameter. - Returns: - Parameter: The retrieved Parameter object. - Raises: KeyError: If the key is not found. + + Returns: + Parameter: The retrieved Parameter object. """ if name in self.parameters: return self.parameters[name] @@ -316,16 +329,22 @@ def __getitem__(self, name: str) -> Parameter: raise KeyError(f"Key '{name}' not found.") def __eq__(self, other: object) -> bool: - """Returns True if all parameters are equal""" + """Return True if all parameters are equal""" if isinstance(other, Parameters): names = set(self.names + other.names) return all(getattr(self, n) == getattr(other, n) for n in names) else: return False - def values_in_fit_limits(self, **kwargs: Any) -> bool: + def values_in_fit_limits(self, **kwargs: Dict) -> bool: """ - Returns True if all values are within the fit limits. + Return True if all values are within the fit limits. + + Keyword Args: + kwargs (dict): The parameter values to check. + + Returns: + bool: True if all values are within the fit limits. """ return all( self.parameters[name].value_in_fit_limits(value) diff --git a/alea/simulators.py b/alea/simulators.py index 8144b406..8e1db352 100644 --- a/alea/simulators.py +++ b/alea/simulators.py @@ -4,8 +4,8 @@ import numpy as np import scipy.stats as sps -import blueice import multihist as mh +from blueice.likelihood import BinnedLogLikelihood, UnbinnedLogLikelihood logging.basicConfig(level=logging.INFO) @@ -14,19 +14,29 @@ class BlueiceDataGenerator: """ A class for generating data from a blueice likelihood term. - Args: - ll_term (blueice.likelihood.BinnedLogLikelihood - or blueice.likelihood.UnbinnedLogLikelihood): - A blueice likelihood term. + Attributes: + ll: The blueice likelihood term. + binned (bool): True if the likelihood term is binned. + bincs (list): The bin centers of the likelihood term. + direction_names (list): The names of the directions of the likelihood term. + source_histograms (list): The histograms of the sources of the likelihood term. + data_lengths (list): The number of bins of each component of the likelihood term. + dtype (list): The data type of the likelihood term. + last_kwargs (dict): The last kwargs used to generate data. + mus: The expected number of events of each source of the likelihood term. + parameters (list): The parameters of the likelihood term. + ll_term (BinnedLogLikelihood or UnbinnedLogLikelihood): A blueice likelihood term. """ def __init__(self, ll_term): - if isinstance(ll_term, blueice.likelihood.BinnedLogLikelihood): - binned = True - elif isinstance(ll_term, blueice.likelihood.UnbinnedLogLikelihood): - binned = False + """Initialize the BlueiceDataGenerator""" + if isinstance(ll_term, BinnedLogLikelihood): + self.binned = True + elif isinstance(ll_term, UnbinnedLogLikelihood): + self.binned = False else: raise NotImplementedError + logging.debug("initing simulator, binned: " + str(self.binned)) ll = deepcopy(ll_term) bins = [] # bin edges @@ -35,7 +45,7 @@ def __init__(self, ll_term): dtype = [] data_length = 1 # number of bins in nD histogram data_lengths = [] # list of number of bins of each component - for direction in ll.base_model.config['analysis_space']: + for direction in ll.base_model.config["analysis_space"]: bins.append(direction[1]) binc = 0.5 * (direction[1][1::] + direction[1][0:-1]) bincs.append(binc) @@ -43,7 +53,7 @@ def __init__(self, ll_term): data_length *= len(direction[1]) - 1 data_lengths.append(len(direction[1]) - 1) direction_names.append(direction[0]) - dtype.append(('source', int)) + dtype.append(("source", int)) logging.debug("init simulate_interpolated with bins: " + str(bins)) data_binc = np.zeros(data_length, dtype=dtype) @@ -57,8 +67,6 @@ def __init__(self, ll_term): source_histograms.append(mh.Histdd(bins=bins)) self.ll = ll - logging.debug("initing simulator, binned: " + str(binned)) - self.binned = binned self.bincs = bincs self.direction_names = direction_names self.source_histograms = source_histograms @@ -68,26 +76,34 @@ def __init__(self, ll_term): self.mus = ll.base_model.expected_events() self.parameters = list(ll.shape_parameters.keys()) self.parameters += [ - n + '_rate_multiplier' for n in ll.rate_parameters.keys() + n + "_rate_multiplier" for n in ll.rate_parameters.keys() ] - def simulate(self, - filter_kwargs=True, - n_toys=None, - sample_n_toys=False, - **kwargs): + def simulate( + self, + filter_kwargs=True, + n_toys=None, + sample_n_toys=False, + **kwargs): """simulate toys for each source Args: - filter_kwargs (bool, optional): If True, only parameters of - the ll object are accepted as kwargs. Defaults to True. - n_toys (int, optional): If not None: a fixed number n_toys of - toys is generated for each source component. + filter_kwargs (bool, optional (default=True)): If True, + only parameters of the ll object are accepted as kwargs. Defaults to True. + n_toys (int, optional (default=None)): If not None, + a fixed number n_toys of toys is generated for each source component. + Defaults to None. + sample_n_toys (bool, optional (default=False)): If True, + the number of toys is sampled from a Poisson distribution with mean n_toys. + Defaults to False. Only works if n_toys is not None. + + Keyword Args: + kwargs: The parameters pasted to the likelihood function. Returns: - structured array: array of simulated data for all sources in - the given analysis space. The index 'source' indicates - the corresponding source of an entry. + numpy.array: Array of simulated data for all sources in the given analysis space. + The index "source" indicates the corresponding source of an entry. + The dtype follows self.dtype. """ if filter_kwargs: kwargs = {k: v for k, v in kwargs.items() if k in self.parameters} @@ -101,7 +117,6 @@ def simulate(self, if type(ret) == float: logging.warning("ERROR, generator kwarg outside range?") logging.warning(kwargs) - #print("ret,kwargs",ret,kwargs) _, mus, ps_array = ret for i in range(len(self.ll.base_model.sources)): self.source_histograms[i].histogram = ps_array[i].reshape( @@ -118,8 +133,8 @@ def simulate(self, if n_toys is not None: if sample_n_toys: self.n_toys_rv = sps.poisson(n_toys).rvs() - n_sources = np.full(len(self.ll.base_model.sources), - self.n_toys_rv) + n_sources = np.full( + len(self.ll.base_model.sources), self.n_toys_rv) else: n_sources = np.full(len(self.ll.base_model.sources), n_toys) else: @@ -129,60 +144,15 @@ def simulate(self, logging.debug("number of events drawn from Poisson: " + str(n_sources)) if len(self.ll.base_model.sources) == 1: n_sources = np.array([n_sources]) + r_data = np.zeros(np.sum(n_sources), dtype=self.dtype) i_write = 0 for i, n_source in enumerate(n_sources): - if 0 < n_source: #dont generate if 0 + if n_source > 0: # dont generate if 0 rvs = self.source_histograms[i].get_random(n_source) for j, n in enumerate(self.direction_names): r_data[n][i_write:i_write + n_source] = rvs[:, j] - r_data['source'][i_write:i_write + n_source] = i + r_data["source"][i_write:i_write + n_source] = i i_write += n_source logging.debug("return simulated data with length: " + str(len(r_data))) return r_data - - -class simulate_nearest: - def __init__(self, ll): - self.ll = ll - self.parameters = list(ll.shape_parameters.keys()) - self.parameters += [ - n + '_rate_multiplier' for n in ll.rate_parameters.keys() - ] - - def simulate(self, **kwargs): - call_args_shape = {} - shape_coordinates = {} - rate_multipliers = {} - for k, v in kwargs.items(): - if k in self.parameters: - if k.endswith('_rate_multiplier'): - rate_multipliers[k.replace('_rate_multiplier', '')] = v - else: - call_args_shape[k] = v - if len(self.ll.shape_parameters) == 0: - return self.ll.base_model.simulate( - rate_multipliers=rate_multipliers) - - for sp in self.ll.shape_parameters.keys(): - if self.ll.shape_parameters[sp][2] is None: - #Non-numerical parameters have their default values in shape_paramters, otherwise in config - shape_coordinates[sp] = self.ll.base_model.config[sp] - else: - shape_coordinates[sp] = self.ll.shape_parameters[sp][2] - call_args_closest = {} - for sp in call_args_shape: - #store key not value (same for numerical, but makes a difference for string nuisance pars) - diff_dir = { - abs(k - call_args_shape[sp]): k - for k, val in self.ll.shape_parameters[sp][0].items() - } - call_args_closest[sp] = diff_dir[min(diff_dir.keys())] - shape_coordinates.update(call_args_closest) - shape_key = [] - for sp in self.ll.shape_parameters.keys(): - shape_key.append(shape_coordinates[sp]) - shape_key = tuple(shape_key) - m = self.ll.anchor_models[shape_key] - - return m.simulate(rate_multipliers=rate_multipliers) diff --git a/alea/template_source.py b/alea/template_source.py index f7dac572..b1322c95 100644 --- a/alea/template_source.py +++ b/alea/template_source.py @@ -358,7 +358,7 @@ def simulate(self, n_events): class SpectrumTemplateSource(blueice.HistogramPdfSource): """ :param spectrum_name: name of bbf json-like spectrum _OR_ function that can be called - templatename #3D histogram (Etrue,S1,S2) to open + templatename #3D histogram (Etrue, S1, S2) to open :param histname: histogram name :param named_parameters: list of config settings to pass to .format on histname and filename """ diff --git a/alea/utils.py b/alea/utils.py index 3d24fe6b..409b78a6 100644 --- a/alea/utils.py +++ b/alea/utils.py @@ -13,6 +13,7 @@ def get_analysis_space(analysis_space: dict) -> list: + """Convert analysis_space to a list of tuples with evaluated values.""" eval_analysis_space = [] for element in analysis_space: @@ -22,9 +23,7 @@ def get_analysis_space(analysis_space: dict) -> list: elif isinstance(value, str): eval_element = ( key, - np.fromstring(value, - dtype=float, - sep=" ")) + np.fromstring(value, dtype=float, sep=" ")) elif isinstance(value, list): eval_element = (key, np.array(value)) else: @@ -42,8 +41,7 @@ def adapt_likelihood_config_for_blueice( Args: likelihood_config (dict): likelihood config dict - template_folder_list (list): list of possible base folders. - Ordered by priority. + template_folder_list (list): list of possible base folders. Ordered by priority. Returns: dict: adapted likelihood config @@ -89,6 +87,12 @@ def formatted_to_asterisked(formatted): Convert formatted string to asterisk Sometimes a parameter(usually shape parameter) is not specified in formatted string, this function replace the parameter with asterisk. + + Args: + formatted (str): formatted string + + Returns: + str: asterisked string """ asterisked = formatted for found in re.findall("\{(.*?)\}", formatted): @@ -105,6 +109,16 @@ def get_file_path(fname, folder_list=None): #. can get file from _get_abspath, return alea internal file path #. can be found in local installed ntauxfiles, return ntauxfiles absolute path #. can be downloaded from MongoDB, download and return cached path + + Args: + fname (str): file name + folder_list (list, optional (default=None)): + list of possible base folders. Ordered by priority. + The function will search for file from the first folder in the list, + and return the first found file immediately without searching the rest folders. + + Returns: + str: full path to the resource file """ if folder_list is None: folder_list = [] diff --git a/docs/make_docs.sh b/docs/make_docs.sh index 593a9e1a..64a7b523 100755 --- a/docs/make_docs.sh +++ b/docs/make_docs.sh @@ -2,4 +2,4 @@ make clean rm -r source/reference sphinx-apidoc -o source/reference ../alea -make html +make html #SPHINXOPTS="-W --keep-going -n" diff --git a/docs/source/build_release_notes.py b/docs/source/build_release_notes.py index dbc04fe8..1fd14762 100644 --- a/docs/source/build_release_notes.py +++ b/docs/source/build_release_notes.py @@ -40,7 +40,7 @@ def convert_release_notes(): target = os.path.join(this_dir, 'reference', 'release_notes.rst') with open(target, 'w') as f: - f.write(header+with_ref) + f.write(header + with_ref) if __name__ == '__main__': diff --git a/docs/source/conf.py b/docs/source/conf.py index 02a92ac4..8421d809 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -13,10 +13,12 @@ # -- General configuration extensions = [ + 'nbsphinx', 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', - 'nbsphinx', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo', ] intersphinx_mapping = { @@ -27,13 +29,24 @@ templates_path = ['_templates'] +# -- Options for NAPOLEON output + +napoleon_include_init_with_doc = True +napoleon_include_private_with_doc = True +napoleon_include_special_with_doc = True + # -- Options for HTML output html_theme = 'sphinx_rtd_theme' # -- Options for EPUB output + epub_show_urls = 'footnote' +# -- Options for TODO output + +todo_include_todos = True + def setup(app): # Hack to import something from this dir. Apparently we're in a weird # situation where you get a __name__ is not in globals KeyError diff --git a/docs/source/notebooks/placeholder.ipynb b/docs/source/notebooks/placeholder.ipynb deleted file mode 120000 index c5208c92..00000000 --- a/docs/source/notebooks/placeholder.ipynb +++ /dev/null @@ -1 +0,0 @@ -../../../notebooks/placeholder.ipynb \ No newline at end of file diff --git a/docs/source/reference/.gitignore b/docs/source/reference/.gitignore deleted file mode 100644 index 5e7d2734..00000000 --- a/docs/source/reference/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore diff --git a/docs/source/reference/alea.examples.rst b/docs/source/reference/alea.examples.rst new file mode 100644 index 00000000..4a928029 --- /dev/null +++ b/docs/source/reference/alea.examples.rst @@ -0,0 +1,21 @@ +alea.examples package +===================== + +Submodules +---------- + +alea.examples.gaussian\_model module +------------------------------------ + +.. automodule:: alea.examples.gaussian_model + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: alea.examples + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/reference/alea.models.rst b/docs/source/reference/alea.models.rst new file mode 100644 index 00000000..423c671d --- /dev/null +++ b/docs/source/reference/alea.models.rst @@ -0,0 +1,21 @@ +alea.models package +=================== + +Submodules +---------- + +alea.models.blueice\_extended\_model module +------------------------------------------- + +.. automodule:: alea.models.blueice_extended_model + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: alea.models + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/reference/alea.rst b/docs/source/reference/alea.rst new file mode 100644 index 00000000..82980323 --- /dev/null +++ b/docs/source/reference/alea.rst @@ -0,0 +1,62 @@ +alea package +============ + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + alea.examples + alea.models + +Submodules +---------- + +alea.model module +----------------- + +.. automodule:: alea.model + :members: + :undoc-members: + :show-inheritance: + +alea.parameters module +---------------------- + +.. automodule:: alea.parameters + :members: + :undoc-members: + :show-inheritance: + +alea.simulators module +---------------------- + +.. automodule:: alea.simulators + :members: + :undoc-members: + :show-inheritance: + +alea.template\_source module +---------------------------- + +.. automodule:: alea.template_source + :members: + :undoc-members: + :show-inheritance: + +alea.utils module +----------------- + +.. automodule:: alea.utils + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: alea + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/reference/modules.rst b/docs/source/reference/modules.rst new file mode 100644 index 00000000..544b6d78 --- /dev/null +++ b/docs/source/reference/modules.rst @@ -0,0 +1,7 @@ +alea +==== + +.. toctree:: + :maxdepth: 4 + + alea diff --git a/notebooks/placeholder.ipynb b/notebooks/placeholder.ipynb index 7d04ed15..fe4e9a0d 100644 --- a/notebooks/placeholder.ipynb +++ b/notebooks/placeholder.ipynb @@ -5,7 +5,83 @@ "id": "76edc00b-f220-47e2-914b-0781c288594d", "metadata": {}, "source": [ - "Please delete this when new meaningful notebooks are added." + "# Please delete this when new meaningful notebooks are added." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a44a6f92-9962-493e-99a7-d2537b14e77b", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from alea.examples import GaussianModel" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c47b23e6-b250-40dc-bd4d-246bbb16f547", + "metadata": {}, + "outputs": [], + "source": [ + "parameter_definition = {\n", + " 'mu': {\n", + " 'fit_guess': 0.,\n", + " 'fittable': True,\n", + " 'nominal_value': 0.,\n", + " },\n", + " 'sigma': {\n", + " 'fit_guess': 1.,\n", + " 'fit_limits': [\n", + " 0.,\n", + " None,\n", + " ],\n", + " 'fittable': True,\n", + " 'nominal_value': 1.,\n", + " },\n", + "}\n", + "model = GaussianModel(parameter_definition=parameter_definition)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5bf427fd-baf9-45d8-86c8-941118d3837b", + "metadata": {}, + "outputs": [], + "source": [ + "model.data = model.generate_data(mu=0, sigma=2)\n", + "best_fit, lf = model.fit(sigma=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "0ceef9f3-caef-483c-99b1-491877b3ca33", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "model.data[0]['hat_mu'] = 1.\n", + "mu = np.linspace(-3, 3, 101)\n", + "plt.plot(mu, model.ll(mu=mu))\n", + "plt.xlabel('mu')\n", + "plt.ylabel('likelihood')\n", + "plt.show()" ] } ], diff --git a/setup.cfg b/setup.cfg index 394a5571..bdad6eef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,6 +27,7 @@ exclude = # D401 First line should be in imperative mood # D403 First word of the first line should be properly capitalized # DAR101 Missing parameter(s) in Docstring: - min_hits +# DAR105 Parameter type malformed # DAR201 Missing "Returns" in Docstring: - return # DAR401 Missing exception(s) in Raises section: -r ValueError # E128 continuation line under-indented for visual indent @@ -74,7 +75,7 @@ exclude = # Q000 Remove bad quotes # E121 continuation line under-indented for hanging indent # E125 continuation line with same indent as next logical line -ignore = C409, C819, D100, D205, D400, D401, D403, DAR101, DAR201, DAR401, E128, E226, E251, F403, F541, I001, I003, I004, I005, P101, WPS110, WPS111, WPS211, WPS122, WPS210, WPS218, WPS229, WPS231, WPS237, WPS237, WPS300, WPS303, WPS304, WPS305, WPS306, WPS317, WPS318, WPS319, WPS326, WPS331, WPS335, WPS336, WPS420, WPS421, WPS432, WPS433, WPS440, WPS435, WPS437, WPS503, WPS504, WPS519, WPS602, Q000, E121, E125 +ignore = C409, C819, D100, D205, D400, D401, D403, DAR101, DAR105, DAR201, DAR401, E128, E226, E251, F403, F541, I001, I003, I004, I005, P101, WPS110, WPS111, WPS211, WPS122, WPS210, WPS218, WPS229, WPS231, WPS237, WPS237, WPS300, WPS303, WPS304, WPS305, WPS306, WPS317, WPS318, WPS319, WPS326, WPS331, WPS335, WPS336, WPS420, WPS421, WPS432, WPS433, WPS440, WPS435, WPS437, WPS503, WPS504, WPS519, WPS602, Q000, E121, E125 per-file-ignores = diff --git a/tests/test_blueice_extended_model.py b/tests/test_blueice_extended_model.py index f7f9ad1c..0c601a91 100644 --- a/tests/test_blueice_extended_model.py +++ b/tests/test_blueice_extended_model.py @@ -1,3 +1,4 @@ +from os import remove import pytest from unittest import TestCase @@ -40,6 +41,11 @@ def test_expectation_values(self): def test_generate_data(self): """Test of the generate_data method""" data = self.model.generate_data() + toydata_file = 'simple_data.hdf5' + self.model.store_data( + toydata_file, + [data for k in self.model.likelihood_names]) + remove(toydata_file) self.assertEqual( len(data), self.n_likelihood_terms + 2) if not (('ancillary_likelihood' in data) and ('generate_values' in data)):