def __init__(self, config_file, random_seed): # Fix the seed for the random number generator np.random.seed(random_seed) # Read in the configuration file using a WorkflowConfigParser. # Note that the argument `configFiles` has to be a list here, # so we need to wrap the `config_file` argument accordingly... config_file = WorkflowConfigParser(configFiles=[config_file]) # Extract variable arguments and constraints # We don't need the static_args here, hence they do not get amended. self.var_args, _ = read_params_from_config(config_file) self.constraints = read_constraints_from_config(config_file) # Extract distributions dist = read_distributions_from_config(config_file) # Extract transformations self.trans = read_transforms_from_config(config_file) # Set up a joint distribution to sample from self.pval = JointDistribution(self.var_args, *dist, **{'constraints': self.constraints})
def from_config(cls, cp, variable_params): """Gets sampling transforms specified in a config file. Sampling parameters and the parameters they replace are read from the ``sampling_params`` section, if it exists. Sampling transforms are read from the ``sampling_transforms`` section(s), using ``transforms.read_transforms_from_config``. An ``AssertionError`` is raised if no ``sampling_params`` section exists in the config file. Parameters ---------- cp : WorkflowConfigParser Config file parser to read. variable_params : list List of parameter names of the original variable params. Returns ------- SamplingTransforms A sampling transforms class. """ if not cp.has_section('sampling_params'): raise ValueError("no sampling_params section found in config file") # get sampling transformations sampling_params, replace_parameters = \ read_sampling_params_from_config(cp) sampling_transforms = transforms.read_transforms_from_config( cp, 'sampling_transforms') logging.info("Sampling in {} in place of {}".format( ', '.join(sampling_params), ', '.join(replace_parameters))) return cls(variable_params, sampling_params, replace_parameters, sampling_transforms)
def __init__( self, config_files: Union[List[Union[str, os.PathLike]], Union[str, os.PathLike]], seed: Optional[int] = None, ): """Class to generate gravitational waveform parameters using PyCBC workflow and distribution packages. """ if seed is not None: raise NotImplementedError( "Reproducible random seed not yet implemented.") self.config_files = config_files if isinstance( config_files, list) else [config_files] self.config_parser = WorkflowConfigParser( configFiles=self.config_files) self.parameters, self.static_args = read_params_from_config( self.config_parser) self.constraints = read_constraints_from_config(self.config_parser) self.transforms = read_transforms_from_config(self.config_parser) self.distribution = JointDistribution( self.parameters, *read_distributions_from_config(self.config_parser), **{'constraints': self.constraints}) # ensure statistics match output of self.parameters self.statistics = compute_parameter_statistics({ parameter: self.distribution.bounds[parameter] for parameter in self.parameters })
def from_config(cls, cp, variable_params): """Gets sampling transforms specified in a config file. Sampling parameters and the parameters they replace are read from the ``sampling_params`` section, if it exists. Sampling transforms are read from the ``sampling_transforms`` section(s), using ``transforms.read_transforms_from_config``. An ``AssertionError`` is raised if no ``sampling_params`` section exists in the config file. Parameters ---------- cp : WorkflowConfigParser Config file parser to read. variable_params : list List of parameter names of the original variable params. Returns ------- SamplingTransforms A sampling transforms class. """ if not cp.has_section('sampling_params'): raise ValueError("no sampling_params section found in config file") # get sampling transformations sampling_params, replace_parameters = \ read_sampling_params_from_config(cp) sampling_transforms = transforms.read_transforms_from_config( cp, 'sampling_transforms') logging.info("Sampling in {} in place of {}".format( ', '.join(sampling_params), ', '.join(replace_parameters))) return cls(variable_params, sampling_params, replace_parameters, sampling_transforms)
def draw_samples_from_config(path, num=1, seed=150914): r""" Generate sampling points from a standalone .ini file. Parameters ---------- path : str The path to the .ini file. num : int The number of samples. seed: int The random seed for sampling. Returns -------- samples : pycbc.io.record.FieldArray The parameter values and names of sample(s). Examples -------- Draw a sample from the distribution defined in the .ini file: >>> import numpy as np >>> from pycbc.distributions.utils import draw_samples_from_config >>> # A path to the .ini file. >>> CONFIG_PATH = "./pycbc_bbh_prior.ini" >>> random_seed = np.random.randint(low=0, high=2**32-1) >>> sample = draw_samples_from_config( >>> path=CONFIG_PATH, num=1, seed=random_seed) >>> # Print all parameters. >>> print(sample.fieldnames) >>> print(sample) >>> # Print a certain parameter, for example 'mass1'. >>> print(sample[0]['mass1']) """ np.random.seed(seed) # Initialise InterpolatingConfigParser class. config_parser = InterpolatingConfigParser() # Read the file file = open(path, 'r') config_parser.read_file(file) file.close() # Construct class that will draw the samples. prior_dists = prior_from_config(cp=config_parser) # Draw samples from prior distribution. samples = prior_dists.rvs(size=int(num)) # Apply parameter transformation. if any(config_parser.get_subsections('waveform_transforms')): waveform_transforms = transforms.read_transforms_from_config( config_parser, 'waveform_transforms') samples = transforms.apply_transforms(samples, waveform_transforms) return samples
def _init_args_from_config(cls, cp): """Helper function for loading parameters. This retrieves the prior, variable parameters, static parameterss, constraints, sampling transforms, and waveform transforms (if provided). Parameters ---------- cp : ConfigParser Config parser to read. Returns ------- dict : Dictionary of the arguments. Has keys ``variable_params``, ``static_params``, ``prior``, and ``sampling_transforms``. If waveform transforms are in the config file, will also have ``waveform_transforms``. """ section = "model" prior_section = "prior" vparams_section = 'variable_params' sparams_section = 'static_params' constraint_section = 'constraint' # check that the name exists and matches name = cp.get(section, 'name') if name != cls.name: raise ValueError("section's {} name does not match mine {}".format( name, cls.name)) # get model parameters variable_params, static_params = distributions.read_params_from_config( cp, prior_section=prior_section, vargs_section=vparams_section, sargs_section=sparams_section) # get prior prior = cls.prior_from_config(cp, variable_params, prior_section, constraint_section) args = { 'variable_params': variable_params, 'static_params': static_params, 'prior': prior } # try to load sampling transforms try: sampling_transforms = SamplingTransforms.from_config( cp, variable_params) except NoSectionError: sampling_transforms = None args['sampling_transforms'] = sampling_transforms # get any waveform transforms if any(cp.get_subsections('waveform_transforms')): logging.info("Loading waveform transforms") args['waveform_transforms'] = \ transforms.read_transforms_from_config( cp, 'waveform_transforms') return args
def __init__(self, config_file, seed=0): numpy.random.seed(seed) config_file = WorkflowConfigParser(config_file, None) var_args, self.static, constraints = read_args_from_config(config_file) dist = read_distributions_from_config(config_file) self.trans = read_transforms_from_config(config_file) self.pval = JointDistribution(var_args, *dist, **{"constraints": constraints})
def _init_args_from_config(cls, cp): """Helper function for loading parameters. This retrieves the prior, variable parameters, static parameterss, constraints, sampling transforms, and waveform transforms (if provided). Parameters ---------- cp : ConfigParser Config parser to read. Returns ------- dict : Dictionary of the arguments. Has keys ``variable_params``, ``static_params``, ``prior``, and ``sampling_transforms``. If waveform transforms are in the config file, will also have ``waveform_transforms``. """ section = "model" prior_section = "prior" vparams_section = 'variable_params' sparams_section = 'static_params' constraint_section = 'constraint' # check that the name exists and matches name = cp.get(section, 'name') if name != cls.name: raise ValueError("section's {} name does not match mine {}".format( name, cls.name)) # get model parameters variable_params, static_params = distributions.read_params_from_config( cp, prior_section=prior_section, vargs_section=vparams_section, sargs_section=sparams_section) # get prior prior = cls.prior_from_config(cp, variable_params, prior_section, constraint_section) args = {'variable_params': variable_params, 'static_params': static_params, 'prior': prior} # try to load sampling transforms try: sampling_transforms = SamplingTransforms.from_config( cp, variable_params) except ValueError: sampling_transforms = None args['sampling_transforms'] = sampling_transforms # get any waveform transforms if any(cp.get_subsections('waveform_transforms')): logging.info("Loading waveform transforms") args['waveform_transforms'] = \ transforms.read_transforms_from_config( cp, 'waveform_transforms') return args
def _init_args_from_config(cls, cp): """Adds loading waveform_transforms to parent function. For details on parameters, see ``from_config``. """ args = super(BaseDataModel, cls)._init_args_from_config(cp) # add waveform transforms to the arguments if any(cp.get_subsections('waveform_transforms')): logging.info("Loading waveform transforms") args['waveform_transforms'] = \ transforms.read_transforms_from_config( cp, 'waveform_transforms') return args
def from_config(cls, cp, section, tag): """ Return instance based on config file Return a new instance based on the config file. This will draw from a single distribution section provided in the config file and apply a single transformation section if desired. If a transformation is applied, an inverse mapping is also provided for use in the config file. """ from pycbc.distributions import read_distributions_from_config from pycbc.transforms import (read_transforms_from_config, apply_transforms, BaseTransform) from pycbc.transforms import transforms as global_transforms params = tag.split(VARARGS_DELIM) subname = cp.get_opt_tag(section, 'subname', tag) size = cp.get_opt_tag(section, 'sample-size', tag) distsec = '{}_sample'.format(subname) dist = read_distributions_from_config(cp, section=distsec) if len(dist) > 1: raise ValueError("Fixed sample distrubtion only supports a single" " distribution to sample from.") logging.info('Drawing samples for fixed sample distribution:%s', params) samples = dist[0].rvs(size=int(float(size))) samples = {p: samples[p] for p in samples.dtype.names} transec = '{}_transform'.format(subname) trans = read_transforms_from_config(cp, section=transec) if len(trans) > 0: trans = trans[0] samples = apply_transforms(samples, [trans]) p1 = samples[params[0]] # We have transformed parameters, so automatically provide the # inverse transform for use in passing to waveform approximants class Thook(BaseTransform): name = subname _inputs = trans.outputs _outputs = trans.inputs p1name = params[0] sort = p1.argsort() p1sorted = p1[sort] def transform(self, maps): idx = numpy.searchsorted(self.p1sorted, maps[self.p1name]) out = {p: samples[p][self.sort[idx]] for p in self.outputs} return self.format_output(maps, out) global_transforms[Thook.name] = Thook return cls(params, samples)
def prior_from_config(cp, prior_section='prior'): """Loads a prior distribution from the given config file. Parameters ---------- cp : pycbc.workflow.WorkflowConfigParser The config file to read. sections : list of str, optional The sections to retrieve the prior from. If ``None`` (the default), will look in sections starting with 'prior'. Returns ------- distributions.JointDistribution The prior distribution. """ # Read variable and static parameters from the config file variable_params, static_params = distributions.read_params_from_config( cp, prior_section=prior_section, vargs_section='variable_params', sargs_section='static_params') # Read waveform_transforms to apply to priors from the config file if any(cp.get_subsections('waveform_transforms')): waveform_transforms = transforms.read_transforms_from_config( cp, 'waveform_transforms') else: waveform_transforms = None # Read constraints to apply to priors from the config file constraints = distributions.read_constraints_from_config( cp, transforms=waveform_transforms, static_args=static_params) # Get PyCBC distribution instances for each variable parameter in the # config file dists = distributions.read_distributions_from_config(cp, prior_section) # construct class that will return draws from the prior return distributions.JointDistribution(variable_params, *dists, **{"constraints": constraints})
def from_config(cls, cp, variable_params): """Gets sampling transforms specified in a config file. Sampling parameters and the parameters they replace are read from the ``sampling_params`` section, if it exists. Sampling transforms are read from the ``sampling_transforms`` section(s), using ``transforms.read_transforms_from_config``. An ``AssertionError`` is raised if no ``sampling_params`` section exists in the config file. Parameters ---------- cp : WorkflowConfigParser Config file parser to read. variable_params : list List of parameter names of the original variable params. Returns ------- SamplingTransforms A sampling transforms class. """ # Check if a sampling_params section is provided try: sampling_params, replace_parameters = \ read_sampling_params_from_config(cp) except NoSectionError as e: logging.warning("No sampling_params section read from config file") raise e # get sampling transformations sampling_transforms = transforms.read_transforms_from_config( cp, 'sampling_transforms') logging.info("Sampling in {} in place of {}".format( ', '.join(sampling_params), ', '.join(replace_parameters))) return cls(variable_params, sampling_params, replace_parameters, sampling_transforms)
def from_config(cls, cp, **kwargs): r"""Initializes an instance of this class from the given config file. Sub-models are initialized before initializing this class. The model section must have a ``submodels`` argument that lists the names of all the submodels to generate as a space-separated list. Each sub-model should have its own ``[{label}__model]`` section that sets up the model for that sub-model. For example: .. code-block:: ini [model] name = hiearchical submodels = event1 event2 [event1__model] <event1 model options> [event2__model] <event2 model options> Similarly, all other sections that are specific to a model should start with the model's label. All sections starting with a model's label will be passed to that model's ``from_config`` method with the label removed from the section name. For example, if a sub-model requires a data section to be specified, it should be titled ``[{label}__data]``. Upon initialization, the ``{label}__`` will be stripped from the section header and passed to the model. No model labels should preceed the ``variable_params``, ``static_params``, ``waveform_transforms``, or ``sampling_transforms`` sections. Instead, the parameters specified in these sections should follow the naming conventions described in :py:class:`HierachicalParam` to determine which sub-model(s) they belong to. (Sampling parameters can follow any naming convention, as they are only handled by the hierarchical model.) This is because the hierarchical model handles all transforms, communication with the sampler, file IO, and prior calculation. Only sub-model's loglikelihood functions are called. Metadata for each sub-model is written to the output hdf file under groups given by the sub-model label. For example, if we have two submodels labelled ``event1`` and ``event2``, there will be groups with the same names in the top level of the output that contain that model's subdata. For instance, if event1 used the ``gaussian_noise`` model, the GW data and PSDs will be found in ``event1/data`` and the low frequency cutoff used for that model will be in the ``attrs`` of the ``event1`` group. Parameters ---------- cp : WorkflowConfigParser Config file parser to read. \**kwargs : All additional keyword arguments are passed to the class. Any provided keyword will override what is in the config file. """ # we need the read from config function from the init; to prevent # circular imports, we import it here from pycbc.inference.models import read_from_config # get the submodels submodel_lbls = shlex.split(cp.get('model', 'submodels')) # sort parameters by model vparam_map = map_params( hpiter(cp.options('variable_params'), submodel_lbls)) sparam_map = map_params( hpiter(cp.options('static_params'), submodel_lbls)) # we'll need any waveform transforms for the initializing sub-models, # as the underlying models will receive the output of those transforms if any(cp.get_subsections('waveform_transforms')): waveform_transforms = transforms.read_transforms_from_config( cp, 'waveform_transforms') wfoutputs = set.union(*[t.outputs for t in waveform_transforms]) wfparam_map = map_params(hpiter(wfoutputs, submodel_lbls)) else: wfparam_map = {lbl: [] for lbl in submodel_lbls} # initialize the models submodels = {} logging.info("Loading submodels") for lbl in submodel_lbls: logging.info("============= %s =============", lbl) # create a config parser to pass to the model subcp = WorkflowConfigParser() # copy sections over that start with the model label (this should # include the [model] section for that model) copy_sections = [ HierarchicalParam(sec, submodel_lbls) for sec in cp.sections() if lbl in sec.split('-')[0].split( HierarchicalParam.delim, 1)[0] ] for sec in copy_sections: # check that the user isn't trying to set variable or static # params for the model (we won't worry about waveform or # sampling transforms here, since that is checked for in the # __init__) if sec.subname in ['variable_params', 'static_params']: raise ValueError("Section {} found in the config file; " "[variable_params] and [static_params] " "sections should not include model " "labels. To specify parameters unique to " "one or more sub-models, prepend the " "individual parameter names with the " "model label. See HierarchicalParam for " "details.".format(sec)) subcp.add_section(sec.subname) for opt, val in cp.items(sec): subcp.set(sec.subname, opt, val) # set the static params subcp.add_section('static_params') for param in sparam_map[lbl]: subcp.set('static_params', param.subname, cp.get('static_params', param.fullname)) # set the variable params: for now we'll just set all the # variable params as static params # so that the model doesn't raise an error looking for # prior sections. We'll then manually set the variable # params after the model is initialized subcp.add_section('variable_params') for param in vparam_map[lbl]: subcp.set('static_params', param.subname, 'REPLACE') # add the outputs from the waveform transforms for param in wfparam_map[lbl]: subcp.set('static_params', param.subname, 'REPLACE') # initialize submodel = read_from_config(subcp) # move the static params back to variable for p in vparam_map[lbl]: submodel.static_params.pop(p.subname) submodel.variable_params = tuple(p.subname for p in vparam_map[lbl]) # remove the waveform transform parameters for p in wfparam_map[lbl]: submodel.static_params.pop(p.subname) # store submodels[lbl] = submodel logging.info("") # now load the model logging.info("Loading hierarchical model") return super().from_config(cp, submodels=submodels)