-
Notifications
You must be signed in to change notification settings - Fork 136
FEAT: add option to crop whitened time series #1030
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5014f4d
c8453d0
d102673
e6c6e3c
45fc893
0e18517
bd9e29e
7d6a399
06adb1e
1e7086e
730a12e
032763b
f3bec33
a52830d
85294b4
6438790
f885be2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -43,6 +43,10 @@ class Interferometer(object): | |||||
| minimum_frequency = PropertyAccessor('strain_data', 'minimum_frequency') | ||||||
| maximum_frequency = PropertyAccessor('strain_data', 'maximum_frequency') | ||||||
| frequency_mask = PropertyAccessor('strain_data', 'frequency_mask') | ||||||
| time_mask = PropertyAccessor('strain_data', 'time_mask') | ||||||
| crop_duration = PropertyAccessor('strain_data', 'crop_duration') | ||||||
| cropped_duration = PropertyAccessor('strain_data', 'cropped_duration') | ||||||
| cropped_frequency_mask = PropertyAccessor('strain_data', 'cropped_frequency_mask') | ||||||
| frequency_domain_strain = PropertyAccessor('strain_data', 'frequency_domain_strain') | ||||||
| time_domain_strain = PropertyAccessor('strain_data', 'time_domain_strain') | ||||||
|
|
||||||
|
|
@@ -597,12 +601,9 @@ def optimal_snr_squared(self, signal): | |||||
|
|
||||||
| Returns | ||||||
| ======= | ||||||
| float: The optimal signal to noise ratio possible squared | ||||||
| float: The optimal signal-to-noise ratio squared of the signal | ||||||
| """ | ||||||
| return gwutils.optimal_snr_squared( | ||||||
| signal=signal[self.strain_data.frequency_mask], | ||||||
| power_spectral_density=self.power_spectral_density_array[self.strain_data.frequency_mask], | ||||||
| duration=self.strain_data.duration) | ||||||
| return (abs(self.whiten_frequency_series(signal))**2).sum() | ||||||
|
|
||||||
| def inner_product(self, signal): | ||||||
| """ | ||||||
|
|
@@ -614,13 +615,13 @@ def inner_product(self, signal): | |||||
|
|
||||||
| Returns | ||||||
| ======= | ||||||
| float: The optimal signal to noise ratio possible squared | ||||||
| float: | ||||||
| The noise-weighted inner product between the passed signal | ||||||
| and the data stored in the :code:`Interferometer`. | ||||||
| """ | ||||||
| return gwutils.noise_weighted_inner_product( | ||||||
| aa=signal[self.strain_data.frequency_mask], | ||||||
| bb=self.strain_data.frequency_domain_strain[self.strain_data.frequency_mask], | ||||||
| power_spectral_density=self.power_spectral_density_array[self.strain_data.frequency_mask], | ||||||
| duration=self.strain_data.duration) | ||||||
| whitened_signal = self.whiten_frequency_series(signal) | ||||||
| whitened_data = self.whitened_frequency_domain_strain | ||||||
| return (whitened_signal.T * whitened_data.conj()).sum() | ||||||
|
|
||||||
| def template_template_inner_product(self, signal_1, signal_2): | ||||||
| """A noise weighted inner product between two templates, using this ifo's PSD. | ||||||
|
|
@@ -636,11 +637,9 @@ def template_template_inner_product(self, signal_1, signal_2): | |||||
| ======= | ||||||
| float: The noise weighted inner product of the two templates | ||||||
| """ | ||||||
| return gwutils.noise_weighted_inner_product( | ||||||
| aa=signal_1[self.strain_data.frequency_mask], | ||||||
| bb=signal_2[self.strain_data.frequency_mask], | ||||||
| power_spectral_density=self.power_spectral_density_array[self.strain_data.frequency_mask], | ||||||
| duration=self.strain_data.duration) | ||||||
| whitened_1 = self.whiten_frequency_series(signal_1) | ||||||
| whitened_2 = self.whiten_frequency_series(signal_2) | ||||||
| return (whitened_1 * whitened_2.conj()).sum() | ||||||
|
|
||||||
| def matched_filter_snr(self, signal): | ||||||
| """ | ||||||
|
|
@@ -655,13 +654,9 @@ def matched_filter_snr(self, signal): | |||||
| complex: The matched filter signal to noise ratio | ||||||
|
|
||||||
| """ | ||||||
| return gwutils.matched_filter_snr( | ||||||
| signal=signal[self.strain_data.frequency_mask], | ||||||
| frequency_domain_strain=self.strain_data.frequency_domain_strain[self.strain_data.frequency_mask], | ||||||
| power_spectral_density=self.power_spectral_density_array[self.strain_data.frequency_mask], | ||||||
| duration=self.strain_data.duration) | ||||||
| return self.inner_product(signal) / self.optimal_snr_squared(signal)**0.5 | ||||||
|
|
||||||
| def whiten_frequency_series(self, frequency_series : np.array) -> np.array: | ||||||
| def whiten_frequency_series(self, frequency_series: np.array) -> np.array: | ||||||
| """Whitens a frequency series with the noise properties of the detector | ||||||
|
|
||||||
| .. math:: | ||||||
|
|
@@ -680,7 +675,21 @@ def whiten_frequency_series(self, frequency_series : np.array) -> np.array: | |||||
| frequency_series : np.array | ||||||
| The frequency series, whitened by the ASD | ||||||
| """ | ||||||
| return frequency_series / (self.amplitude_spectral_density_array * np.sqrt(self.duration / 4)) | ||||||
| if self.crop_duration == 0: | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens (or can) crop_duration be "None" or
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would fail in the |
||||||
| return gwutils.frequency_domain_whiten( | ||||||
| frequency_series=frequency_series, | ||||||
| amplitude_spectral_density=self.amplitude_spectral_density_array, | ||||||
| frequency_mask=self.frequency_mask, | ||||||
| duration=self.duration, | ||||||
| ) | ||||||
| else: | ||||||
| return gwutils.whiten_and_crop( | ||||||
| frequency_series=frequency_series, | ||||||
| amplitude_spectral_density=self.amplitude_spectral_density_array, | ||||||
| frequency_mask=self.frequency_mask, | ||||||
| time_mask=self.time_mask, | ||||||
| duration=self.duration, | ||||||
| ) | ||||||
|
|
||||||
| def get_whitened_time_series_from_whitened_frequency_series( | ||||||
| self, | ||||||
|
|
@@ -718,7 +727,9 @@ def get_whitened_time_series_from_whitened_frequency_series( | |||||
|
|
||||||
| whitened_time_series = ( | ||||||
| np.fft.irfft(whitened_frequency_series) | ||||||
| * np.sqrt(np.sum(self.frequency_mask)) / frequency_window_factor | ||||||
| * np.sqrt(np.sum(self.frequency_mask)) | ||||||
| / frequency_window_factor | ||||||
| * self.time_mask.mean()**0.5 | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does this use both
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I was trying to avoid explicitly using numpy, but that's unavoidable since we need an fft and sqrt is sometimes meaningfully faster.
Suggested change
|
||||||
| ) | ||||||
|
|
||||||
| return whitened_time_series | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ | |
| from ...core.prior import Interped, Prior, Uniform, DeltaFunction | ||
| from ..detector import InterferometerList, get_empty_interferometer, calibration | ||
| from ..prior import BBHPriorDict, Cosmological | ||
| from ..utils import noise_weighted_inner_product, zenith_azimuth_to_ra_dec, ln_i0 | ||
| from ..utils import zenith_azimuth_to_ra_dec, ln_i0 | ||
|
|
||
|
|
||
| class GravitationalWaveTransient(Likelihood): | ||
|
|
@@ -277,63 +277,55 @@ def calculate_snrs(self, waveform_polarizations, interferometer, *, return_array | |
| interferometer=interferometer, | ||
| parameters=parameters, | ||
| ) | ||
| _mask = interferometer.frequency_mask | ||
|
|
||
| if 'recalib_index' in parameters: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does the signal no longer require masking here?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unclear, is this tested? |
||
| signal[_mask] *= self.calibration_draws[interferometer.name][int(parameters['recalib_index'])] | ||
| signal *= self.calibration_draws[interferometer.name][int(parameters['recalib_index'])] | ||
|
|
||
| d_inner_h = interferometer.inner_product(signal=signal) | ||
| optimal_snr_squared = interferometer.optimal_snr_squared(signal=signal) | ||
| whitened_signal = interferometer.whiten_frequency_series(signal) | ||
|
|
||
| d_inner_h = (interferometer.whitened_frequency_domain_strain.conjugate() * whitened_signal).sum() | ||
| optimal_snr_squared = (abs(whitened_signal)**2).sum() | ||
|
GregoryAshton marked this conversation as resolved.
|
||
| complex_matched_filter_snr = d_inner_h / (optimal_snr_squared**0.5) | ||
|
|
||
| d_inner_h_array = None | ||
| optimal_snr_squared_array = None | ||
|
|
||
| normalization = 4 / self.waveform_generator.duration | ||
|
|
||
| if return_array is False: | ||
| d_inner_h_array = None | ||
| optimal_snr_squared_array = None | ||
| elif self.time_marginalization and self.calibration_marginalization: | ||
|
|
||
| d_inner_h_integrand = np.tile( | ||
| interferometer.frequency_domain_strain.conjugate() * signal / | ||
| interferometer.power_spectral_density_array, (self.number_of_response_curves, 1)).T | ||
| interferometer.whitened_frequency_domain_strain.conjugate() * whitened_signal, | ||
| (self.number_of_response_curves, 1) | ||
| ).T | ||
|
|
||
| d_inner_h_integrand[_mask] *= self.calibration_draws[interferometer.name].T | ||
| d_inner_h_integrand[interferometer.frequency_mask] *= self.calibration_draws[interferometer.name].T | ||
|
|
||
| d_inner_h_array = 4 / self.waveform_generator.duration * np.fft.fft( | ||
| d_inner_h_integrand[0:-1], axis=0 | ||
| ).T | ||
| d_inner_h_array = np.fft.fft(d_inner_h_integrand[:-1], axis=0).T | ||
|
|
||
| optimal_snr_squared_integrand = ( | ||
| normalization * np.abs(signal)**2 / interferometer.power_spectral_density_array | ||
| ) | ||
| optimal_snr_squared_integrand = np.abs(whitened_signal)**2 | ||
| optimal_snr_squared_array = np.dot( | ||
| optimal_snr_squared_integrand[_mask], | ||
| optimal_snr_squared_integrand[interferometer.frequency_mask], | ||
| self.calibration_abs_draws[interferometer.name].T | ||
| ) | ||
|
|
||
| elif self.time_marginalization and not self.calibration_marginalization: | ||
| d_inner_h_array = normalization * np.fft.fft( | ||
| signal[0:-1] | ||
| * interferometer.frequency_domain_strain.conjugate()[0:-1] | ||
| / interferometer.power_spectral_density_array[0:-1] | ||
| d_inner_h_integrand = ( | ||
| whitened_signal | ||
| * interferometer.whitened_frequency_domain_strain.conjugate() | ||
| ) | ||
| d_inner_h_array = np.fft.fft(d_inner_h_integrand[:-1]) | ||
|
|
||
| elif self.calibration_marginalization and ('recalib_index' not in parameters): | ||
| d_inner_h_integrand = ( | ||
| normalization * | ||
| interferometer.frequency_domain_strain.conjugate() * signal | ||
| / interferometer.power_spectral_density_array | ||
| ) | ||
| d_inner_h_array = np.dot(d_inner_h_integrand[_mask], self.calibration_draws[interferometer.name].T) | ||
| interferometer.whitened_frequency_domain_strain.conjugate() * whitened_signal | ||
| )[interferometer.frequency_mask] | ||
| d_inner_h_array = np.dot(d_inner_h_integrand, self.calibration_draws[interferometer.name].T) | ||
|
|
||
| optimal_snr_squared_integrand = ( | ||
| normalization * np.abs(signal)**2 / interferometer.power_spectral_density_array | ||
| ) | ||
| optimal_snr_squared_integrand = np.abs(whitened_signal)**2 | ||
| optimal_snr_squared_array = np.dot( | ||
| optimal_snr_squared_integrand[_mask], | ||
| optimal_snr_squared_integrand[interferometer.frequency_mask], | ||
| self.calibration_abs_draws[interferometer.name].T | ||
| ) | ||
|
|
||
|
|
@@ -390,12 +382,9 @@ def priors(self, priors): | |
| def _calculate_noise_log_likelihood(self): | ||
| log_l = 0 | ||
| for interferometer in self.interferometers: | ||
| mask = interferometer.frequency_mask | ||
| log_l -= noise_weighted_inner_product( | ||
| interferometer.frequency_domain_strain[mask], | ||
| interferometer.frequency_domain_strain[mask], | ||
| interferometer.power_spectral_density_array[mask], | ||
| self.waveform_generator.duration) / 2 | ||
| log_l -= ( | ||
|
GregoryAshton marked this conversation as resolved.
|
||
| abs(interferometer.whitened_frequency_domain_strain)**2 | ||
| ).sum() / 2 | ||
| return float(np.real(log_l)) | ||
|
|
||
| def noise_log_likelihood(self): | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.