Skip to content
Snippets Groups Projects
Commit 7f47d2df authored by Colm Talbot's avatar Colm Talbot
Browse files

Merge branch 'master' of git.ligo.org:Monash/tupak

parents fae31b51 83ef6123
No related branches found
No related tags found
No related merge requests found
...@@ -22,11 +22,11 @@ exitcode-jessie: ...@@ -22,11 +22,11 @@ exitcode-jessie:
- pip install coverage - pip install coverage
- pip install coverage-badge - pip install coverage-badge
- coverage erase - coverage erase
- coverage run -a test/prior_tests.py - coverage run --include=tupak/* -a test/prior_tests.py
- coverage run -a test/tests.py - coverage run --include=tupak/* -a test/tests.py
- coverage run -a test/waveform_generator_tests.py - coverage run --include=tupak/* -a test/waveform_generator_tests.py
- coverage run -a test/noise_realisation_tests.py - coverage run --include=tupak/* -a test/noise_realisation_tests.py
- coverage html --include=tupak/* - coverage html
- coverage-badge -o coverage_badge.svg -f - coverage-badge -o coverage_badge.svg -f
artifacts: artifacts:
paths: paths:
......
[![pipeline status](https://git.ligo.org/Monash/tupak/badges/master/pipeline.svg)](https://git.ligo.org/Monash/tupak/commits/master) [![pipeline status](https://git.ligo.org/Monash/tupak/badges/master/pipeline.svg)](https://git.ligo.org/Monash/tupak/commits/master)
[![coverage report](https://monash.docs.ligo.org/tupak/coverage_report.svg)]( [![coverage report](https://monash.docs.ligo.org/tupak/coverage_badge.svg)](
https://monash.docs.ligo.org/tupak/) https://monash.docs.ligo.org/tupak/)
# Tupak # Tupak
......
...@@ -20,7 +20,7 @@ outdir = 'outdir' ...@@ -20,7 +20,7 @@ outdir = 'outdir'
# use of the `tupak` waveform_generator to make the signal (more on this later) # use of the `tupak` waveform_generator to make the signal (more on this later)
# But, one could make this work without the waveform generator. # But, one could make this work without the waveform generator.
class GaussianGravitationalWaveTransient(): class GaussianLikelihood():
def __init__(self, x, y, waveform_generator): def __init__(self, x, y, waveform_generator):
self.x = x self.x = x
self.y = y self.y = y
...@@ -76,7 +76,7 @@ waveform_generator = tupak.waveform_generator.WaveformGenerator(time_duration=ti ...@@ -76,7 +76,7 @@ waveform_generator = tupak.waveform_generator.WaveformGenerator(time_duration=ti
# Now lets instantiate a version of out GravitationalWaveTransient, giving it the time, data # Now lets instantiate a version of out GravitationalWaveTransient, giving it the time, data
# and waveform_generator # and waveform_generator
likelihood = GaussianGravitationalWaveTransient(time, data, waveform_generator) likelihood = GaussianLikelihood(time, data, waveform_generator)
# From hereon, the syntax is exactly equivalent to other tupak examples # From hereon, the syntax is exactly equivalent to other tupak examples
# We make a prior # We make a prior
......
...@@ -198,6 +198,7 @@ class TestFillPrior(unittest.TestCase): ...@@ -198,6 +198,7 @@ class TestFillPrior(unittest.TestCase):
def setUp(self): def setUp(self):
self.likelihood = Mock() self.likelihood = Mock()
self.likelihood.parameters = dict(a=0, b=0, c=0, d=0, asdf=0, ra=1) self.likelihood.parameters = dict(a=0, b=0, c=0, d=0, asdf=0, ra=1)
self.likelihood.non_standard_sampling_parameter_keys = dict(t=8)
self.priors = dict(a=1, b=1.1, c='string', d=tupak.prior.Uniform(0, 1)) self.priors = dict(a=1, b=1.1, c='string', d=tupak.prior.Uniform(0, 1))
self.priors = tupak.prior.fill_priors(self.priors, self.likelihood) self.priors = tupak.prior.fill_priors(self.priors, self.likelihood)
......
...@@ -57,8 +57,10 @@ class Test(unittest.TestCase): ...@@ -57,8 +57,10 @@ class Test(unittest.TestCase):
priors['luminosity_distance'] = tupak.prior.Uniform( priors['luminosity_distance'] = tupak.prior.Uniform(
name='luminosity_distance', minimum=dL - 10, maximum=dL + 10) name='luminosity_distance', minimum=dL - 10, maximum=dL + 10)
result = tupak.sampler.run_sampler(likelihood, priors, sampler='nestle') result = tupak.sampler.run_sampler(
self.assertAlmostEqual(np.mean(result.samples), dL, delta=np.std(result.samples)) likelihood, priors, sampler='nestle', verbose=False, npoints=100)
self.assertAlmostEqual(np.mean(result.samples), dL,
delta=np.std(result.samples))
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -11,7 +11,20 @@ import tupak ...@@ -11,7 +11,20 @@ import tupak
import logging import logging
class GravitationalWaveTransient(object): class Likelihood(object):
""" Empty likelihood class to be subclassed by other likelihoods """
def log_likelihood():
return np.nan
def noise_log_likelihood():
return np.nan
def log_likelihood_ratio(self):
return self.log_likelihood() - self.noise_log_likelihood()
class GravitationalWaveTransient(Likelihood):
""" A gravitational-wave transient likelihood object """ A gravitational-wave transient likelihood object
This is the usual likelihood object to use for transient gravitational This is the usual likelihood object to use for transient gravitational
...@@ -114,7 +127,7 @@ class GravitationalWaveTransient(object): ...@@ -114,7 +127,7 @@ class GravitationalWaveTransient(object):
return log_l.real return log_l.real
def log_likelihood(self): def log_likelihood(self):
return self.log_likelihood() + self.noise_log_likelihood() return self.log_likelihood_ratio() + self.noise_log_likelihood()
def setup_distance_marginalization(self): def setup_distance_marginalization(self):
if 'luminosity_distance' not in self.prior.keys(): if 'luminosity_distance' not in self.prior.keys():
...@@ -136,6 +149,62 @@ class GravitationalWaveTransient(object): ...@@ -136,6 +149,62 @@ class GravitationalWaveTransient(object):
bounds_error=False, fill_value=-np.inf) bounds_error=False, fill_value=-np.inf)
class BasicGravitationalWaveTransient(Likelihood):
""" A basic gravitaitonal wave transient likelihood
The simplest frequency-domain gravitational wave transient likelihood. Does
not include distance/phase marginalization.
Parameters
----------
interferometers: list
A list of `tupak.detector.Interferometer` instances - contains the
detector data and power spectral densities
waveform_generator: `tupak.waveform_generator.WaveformGenerator`
An object which computes the frequency-domain strain of the signal,
given some set of parameters
Returns
-------
Likelihood: `tupak.likelihood.Likelihood`
A likehood object, able to compute the likelihood of the data given
some model parameters
"""
def __init__(self, interferometers, waveform_generator):
self.interferometers = interferometers
self.waveform_generator = waveform_generator
def noise_log_likelihood(self):
log_l = 0
for interferometer in self.interferometers:
log_l -= 2. / self.waveform_generator.time_duration * np.sum(
abs(interferometer.data) ** 2 /
interferometer.power_spectral_density_array)
return log_l.real
def log_likelihood(self):
log_l = 0
waveform_polarizations = self.waveform_generator.frequency_domain_strain()
if waveform_polarizations is None:
return np.nan_to_num(-np.inf)
for interferometer in self.interferometers:
log_l += self.log_likelihood_interferometer(
waveform_polarizations, interferometer)
return log_l.real
def log_likelihood_interferometer(self, waveform_polarizations,
interferometer):
signal_ifo = interferometer.get_detector_response(
waveform_polarizations, self.waveform_generator.parameters)
log_l = - 2. / self.waveform_generator.time_duration * np.vdot(
interferometer.data - signal_ifo,
(interferometer.data - signal_ifo)
/ interferometer.power_spectral_density_array)
return log_l.real
def get_binary_black_hole_likelihood(interferometers): def get_binary_black_hole_likelihood(interferometers):
""" A rapper to quickly set up a likelihood for BBH parameter estimation """ A rapper to quickly set up a likelihood for BBH parameter estimation
...@@ -157,3 +226,4 @@ def get_binary_black_hole_likelihood(interferometers): ...@@ -157,3 +226,4 @@ def get_binary_black_hole_likelihood(interferometers):
parameters={'waveform_approximant': 'IMRPhenomPv2', 'reference_frequency': 50}) parameters={'waveform_approximant': 'IMRPhenomPv2', 'reference_frequency': 50})
likelihood = tupak.likelihood.GravitationalWaveTransient(interferometers, waveform_generator) likelihood = tupak.likelihood.GravitationalWaveTransient(interferometers, waveform_generator)
return likelihood return likelihood
...@@ -433,7 +433,7 @@ def parse_keys_to_parameters(keys): ...@@ -433,7 +433,7 @@ def parse_keys_to_parameters(keys):
return parameters return parameters
def fill_priors(prior, likelihood, parameters=None): def fill_priors(prior, likelihood):
""" """
Fill dictionary of priors based on required parameters of likelihood Fill dictionary of priors based on required parameters of likelihood
...@@ -446,9 +446,9 @@ def fill_priors(prior, likelihood, parameters=None): ...@@ -446,9 +446,9 @@ def fill_priors(prior, likelihood, parameters=None):
dictionary of prior objects and floats dictionary of prior objects and floats
likelihood: tupak.likelihood.GravitationalWaveTransient instance likelihood: tupak.likelihood.GravitationalWaveTransient instance
Used to infer the set of parameters to fill the prior with Used to infer the set of parameters to fill the prior with
parameters: list
list of parameters to be sampled in, this can override the default Note: if `likelihood` has `non_standard_sampling_parameter_keys`, then this
priors for the waveform generator will set-up default priors for those as well.
Returns Returns
------- -------
...@@ -470,8 +470,8 @@ def fill_priors(prior, likelihood, parameters=None): ...@@ -470,8 +470,8 @@ def fill_priors(prior, likelihood, parameters=None):
missing_keys = set(likelihood.parameters) - set(prior.keys()) missing_keys = set(likelihood.parameters) - set(prior.keys())
if parameters is not None: if getattr(likelihood, 'non_standard_sampling_parameter_keys', None) is not None:
for parameter in parameters: for parameter in likelihood.non_standard_sampling_parameter_keys:
prior[parameter] = create_default_prior(parameter) prior[parameter] = create_default_prior(parameter)
for missing_key in missing_keys: for missing_key in missing_keys:
......
...@@ -67,6 +67,9 @@ class Result(dict): ...@@ -67,6 +67,9 @@ class Result(dict):
else: else:
return '' return ''
def get_result_dictionary(self):
return dict(self)
def save_to_file(self, outdir, label): def save_to_file(self, outdir, label):
""" Writes the Result to a deepdish h5 file """ """ Writes the Result to a deepdish h5 file """
file_name = result_file_name(outdir, label) file_name = result_file_name(outdir, label)
...@@ -80,7 +83,7 @@ class Result(dict): ...@@ -80,7 +83,7 @@ class Result(dict):
logging.info("Saving result to {}".format(file_name)) logging.info("Saving result to {}".format(file_name))
try: try:
deepdish.io.save(file_name, self) deepdish.io.save(file_name, self.get_result_dictionary())
except Exception as e: except Exception as e:
logging.error( logging.error(
"\n\n Saving the data has failed with the following message:\n {} \n\n" "\n\n Saving the data has failed with the following message:\n {} \n\n"
......
...@@ -245,8 +245,9 @@ class Nestle(Sampler): ...@@ -245,8 +245,9 @@ class Nestle(Sampler):
def run_sampler(self): def run_sampler(self):
nestle = self.external_sampler nestle = self.external_sampler
self.external_sampler_function = nestle.sample self.external_sampler_function = nestle.sample
if self.kwargs.get('verbose', True): if 'verbose' in self.kwargs:
self.kwargs['callback'] = nestle.print_progress if self.kwargs['verbose']:
self.kwargs['callback'] = nestle.print_progress
self.kwargs.pop('verbose') self.kwargs.pop('verbose')
self.verify_kwargs_against_external_sampler_function() self.verify_kwargs_against_external_sampler_function()
...@@ -444,7 +445,7 @@ def run_sampler(likelihood, priors=None, label='label', outdir='outdir', ...@@ -444,7 +445,7 @@ def run_sampler(likelihood, priors=None, label='label', outdir='outdir',
if priors is None: if priors is None:
priors = dict() priors = dict()
priors = fill_priors(priors, likelihood, parameters=likelihood.non_standard_sampling_parameter_keys) priors = fill_priors(priors, likelihood)
tupak.prior.write_priors_to_file(priors, outdir) tupak.prior.write_priors_to_file(priors, outdir)
if implemented_samplers.__contains__(sampler.title()): if implemented_samplers.__contains__(sampler.title()):
...@@ -468,7 +469,7 @@ def run_sampler(likelihood, priors=None, label='label', outdir='outdir', ...@@ -468,7 +469,7 @@ def run_sampler(likelihood, priors=None, label='label', outdir='outdir',
result.injection_parameters = injection_parameters result.injection_parameters = injection_parameters
if conversion_function is not None: if conversion_function is not None:
conversion_function(result.injection_parameters) conversion_function(result.injection_parameters)
result.fixed_parameter_keys = [key for key in priors if isinstance(key, prior.DeltaFunction)] result.fixed_parameter_keys = sampler.fixed_parameter_keys
# result.prior = prior # Removed as this breaks the saving of the data # result.prior = prior # Removed as this breaks the saving of the data
result.samples_to_data_frame(likelihood=likelihood, priors=priors, conversion_function=conversion_function) result.samples_to_data_frame(likelihood=likelihood, priors=priors, conversion_function=conversion_function)
result.kwargs = sampler.kwargs result.kwargs = sampler.kwargs
......
...@@ -89,7 +89,7 @@ class WaveformGenerator(object): ...@@ -89,7 +89,7 @@ class WaveformGenerator(object):
if self.parameter_conversion is not None: if self.parameter_conversion is not None:
for key in added_keys: for key in added_keys:
self.parameters.pop(key) self.parameters.pop(key)
return model_time_series[key] return model_time_series
@property @property
def frequency_array(self): def frequency_array(self):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment