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})
Beispiel #2
0
    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
        })
Beispiel #3
0
    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})   
Beispiel #4
0
    def setUp(self):
        ###### Get data for references analysis of 170817
        m = Merger("GW170817")
        ifos = ['H1', 'V1', 'L1']
        self.psds = {}
        self.data = {}

        for ifo in ifos:
            print("Processing {} data".format(ifo))

            # Download the gravitational wave data for GW170817
            url = "https://dcc.ligo.org/public/0146/P1700349/001/"
            url += "{}-{}1_LOSC_CLN_4_V1-1187007040-2048.gwf"
            fname = download_file(url.format(ifo[0], ifo[0]), cache=True)
            ts = read_frame(fname,
                            "{}:LOSC-STRAIN".format(ifo),
                            start_time=int(m.time - 260),
                            end_time=int(m.time + 40))
            ts = highpass(ts, 15.0)
            ts = resample_to_delta_t(ts, 1.0 / 2048)
            ts = ts.time_slice(m.time - 112, m.time + 16)
            self.data[ifo] = ts.to_frequencyseries()

            psd = interpolate(ts.psd(4), ts.delta_f)
            psd = inverse_spectrum_truncation(psd,
                                              int(4 * psd.sample_rate),
                                              trunc_method='hann',
                                              low_frequency_cutoff=20.0)
            self.psds[ifo] = psd

        self.static = {
            'mass1': 1.3757,
            'mass2': 1.3757,
            'f_lower': 20.0,
            'approximant': "TaylorF2",
            'polarization': 0,
            'ra': 3.44615914,
            'dec': -0.40808407,
            'tc': 1187008882.42840,
        }
        self.variable = (
            'distance',
            'inclination',
        )
        self.flow = {'H1': 25, 'L1': 25, 'V1': 25}
        inclination_prior = SinAngle(inclination=None)
        distance_prior = Uniform(distance=(10, 100))
        tc_prior = Uniform(tc=(m.time - 0.1, m.time + 0.1))
        self.prior = JointDistribution(self.variable, inclination_prior,
                                       distance_prior)

        ###### Expected answers
        # Answer taken from marginalized gaussian model
        self.q1 = {'distance': 42.0, 'inclination': 2.5}
        self.a1 = 541.8235746138382

        # answer taken from brute marginize pol + phase
        self.a2 = 542.581
        self.pol_samples = 200
Beispiel #5
0
class WaveformParameterGenerator(object):
    """
    :class:`WaveformParameterGenerator` objects are essentially just a
    simple convenience wrapper to construct the joint probability
    distribution (and provide a method to draw samples from it) of the
    parameters specified by the `[variable_args]` section of an
    `*.ini` configuration file and their distributions as defined in
    the corresponding `[prior-*]` sections.
    
    Args:
        config_file (str): Path to the `*.ini` configuration file,
            which contains the information about the parameters to be
            generated and their distributions.
        random_seed (int): Seed for the random number generator.
            Caveat: We can only set the seed of the global numpy RNG.
    """
    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 draw(self):
        """
        Draw a sample from the joint distribution and construct a
        dictionary that maps the parameter names to the values
        generated for them.

        Returns:
            A `dict` containing a the names and values of a set of
            randomly sampled waveform parameters (e.g., masses, spins,
            position in the sky, ...).
        """
        values = apply_transforms(self.pval.rvs(), self.trans)[0]
        result = dict(zip(self.var_args, values))

        return result
Beispiel #6
0
class WFParamGenerator(object):
    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 draw(self):
        return apply_transforms(self.pval.rvs(), self.trans)[0]
Beispiel #7
0
    def setup_distance_marginalization(self,
                                       variable_params,
                                       marginalize_phase=False,
                                       marginalize_distance=False,
                                       marginalize_distance_param='distance',
                                       marginalize_distance_samples=int(1e4),
                                       marginalize_distance_interpolator=False,
                                       marginalize_distance_snr_range=None,
                                       marginalize_distance_density=None,
                                       **kwargs):
        """ Setup the model for use with distance marginalization

        This function sets up precalculations for distance / phase
        marginalization. For distance margininalization it modifies the
        model to internally remove distance as a parameter.

        Parameters
        ----------
        variable_params: list of strings
            The set of variable parameters
        marginalize_phase: bool, False
            Do analytic marginalization (appopriate only for 22 mode waveforms)
        marginalize_distance: bool, False
            Marginalize over distance
        marginalize_distance_param: str
            Name of the parameter that is used to determine the distance.
            This might be 'distance' or a parameter which can be converted
            to distance by a provided univariate transformation.
        marginalize_distance_interpolator: bool
            Use a pre-calculated interpolating function for the distance
            marginalized likelihood.
        marginalize_distance_snr_range: tuple of floats, (1, 50)
            The SNR range for the interpolating function to be defined in.
            If a sampler goes outside this range, the logl will be returned
            as -numpy.inf.
        marginalize_distance_density: tuple of intes, (1000, 1000)
            The dimensions of the interpolation grid over (sh, hh).

        Returns
        -------
        variable_params: list of strings
            Set of variable params (missing distance-related parameter).
        kwags: dict
            The keyword arguments to the model initialization, may be modified
            from the original set by this function.
        """
        self.marginalize_phase = marginalize_phase
        self.distance_marginalization = False
        self.distance_interpolator = None
        if not marginalize_distance:
            return variable_params, kwargs

        if isinstance(marginalize_distance_snr_range, str):
            marginalize_distance_snr_range = \
                str_to_tuple(marginalize_distance_snr_range, float)

        if isinstance(marginalize_distance_density, str):
            marginalize_distance_density = \
                str_to_tuple(marginalize_distance_density, int)

        logging.info('Marginalizing over distance')

        # Take distance out of the variable params since we'll handle it
        # manually now
        variable_params.remove(marginalize_distance_param)
        old_prior = kwargs['prior']
        dists = [
            d for d in old_prior.distributions
            if marginalize_distance_param not in d.params
        ]
        dprior = [
            d for d in old_prior.distributions
            if marginalize_distance_param in d.params
        ][0]
        prior = JointDistribution(variable_params, *dists, **old_prior.kwargs)
        kwargs['prior'] = prior
        if len(dprior.params) != 1 or not hasattr(dprior, 'bounds'):
            raise ValueError('Distance Marginalization requires a '
                             'univariate and bounded prior')

        # Set up distance prior vector and samples

        # (1) prior is using distance
        if dprior.params[0] == 'distance':
            logging.info(
                "Prior is directly on distance, setting up "
                "%s grid weights", marginalize_distance_samples)
            dmin, dmax = dprior.bounds['distance']
            dist_locs = numpy.linspace(dmin, dmax,
                                       int(marginalize_distance_samples))
            dist_weights = [dprior.pdf(distance=l) for l in dist_locs]
            dist_weights = numpy.array(dist_weights)

        # (2) prior is univariate and can be converted to distance
        elif marginalize_distance_param != 'distance':
            waveform_transforms = kwargs['waveform_transforms']
            pname = dprior.params[0]
            logging.info("Settings up transform,  prior is in terms of"
                         " %s", pname)
            wtrans = [
                d for d in waveform_transforms if 'distance' not in d.outputs
            ]
            if len(wtrans) == 0:
                wtrans = None
            kwargs['waveform_transforms'] = wtrans
            dtrans = [
                d for d in waveform_transforms if 'distance' in d.outputs
            ][0]
            v = dprior.rvs(int(1e8))
            d = dtrans.transform({pname: v[pname]})['distance']
            d.sort()
            cdf = numpy.arange(1, len(d) + 1) / len(d)
            i = interp1d(d, cdf)
            dmin, dmax = d.min(), d.max()
            logging.info('Distance range %s-%s', dmin, dmax)
            x = numpy.linspace(dmin, dmax,
                               int(marginalize_distance_samples) + 1)
            xl, xr = x[:-1], x[1:]
            dist_locs = 0.5 * (xr + xl)
            dist_weights = i(xr) - i(xl)
        else:
            raise ValueError("No prior seems to determine the distance")

        dist_weights /= dist_weights.sum()
        dist_ref = 0.5 * (dmax + dmin)
        self.distance_marginalization = dist_ref / dist_locs, dist_weights
        self.distance_interpolator = None
        if marginalize_distance_interpolator:
            setup_args = {}
            if marginalize_distance_snr_range:
                setup_args['snr_range'] = marginalize_distance_snr_range
            if marginalize_distance_density:
                setup_args['density'] = marginalize_distance_density
            i = setup_distance_marg_interpolant(self.distance_marginalization,
                                                phase=self.marginalize_phase,
                                                **setup_args)
            self.distance_interpolator = i
        kwargs['static_params']['distance'] = dist_ref
        return variable_params, kwargs
Beispiel #8
0
class ParameterGenerator:
    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
        })

    @property
    def latex(self):
        return [
            get_parameter_latex_labels()[param] for param in self.parameters
        ]

    def draw(self,
             n: int = 1,
             as_df: bool = False) -> Union[pd.DataFrame, np.record]:
        """
        Draw a sample from the joint distribution and construct a
        dictionary that maps the parameter names to the values
        generated for them.
        
        Arguments:
            n: int
                The number of samples to draw.
            as_dataframe: bool
                If True, returns a pd.DataFrame; else a numpy.recarray


        Returns:
            A `dict` containing a the names and values of a set of
            randomly sampled waveform parameters (e.g., masses, spins,
            position in the sky, ...).
        """
        assert n >= 1, "n must be a positive integer."
        samples = apply_transforms(self.distribution.rvs(size=n),
                                   self.transforms)
        if as_df:
            return pd.DataFrame(samples)
        return samples