diff --git a/bilby/core/utils/calculus.py b/bilby/core/utils/calculus.py index 490195930..5fea6982a 100644 --- a/bilby/core/utils/calculus.py +++ b/bilby/core/utils/calculus.py @@ -1,8 +1,7 @@ import math -import array_api_compat as aac import numpy as np -from scipy.interpolate import RectBivariateSpline, interp1d as _interp1d +from scipy.interpolate import interp1d as _interp1d from scipy.special import logsumexp from .log import logger @@ -218,64 +217,6 @@ def __call__(self, x): ) -class BoundedRectBivariateSpline(RectBivariateSpline): - - def __init__(self, x, y, z, bbox=[None] * 4, kx=3, ky=3, s=0, fill_value=None): - self.x_min, self.x_max, self.y_min, self.y_max = bbox - if self.x_min is None: - self.x_min = min(x) - if self.x_max is None: - self.x_max = max(x) - if self.y_min is None: - self.y_min = min(y) - if self.y_max is None: - self.y_max = max(y) - self.fill_value = fill_value - self.x = x - self.y = y - self.z = z - super().__init__(x=x, y=y, z=z, bbox=bbox, kx=kx, ky=ky, s=s) - - def __call__(self, x, y, dx=0, dy=0, grid=False): - xp = array_module([x, y]) - if aac.is_numpy_namespace(xp): - return self._call_scipy(x, y, dx=dx, dy=dy, grid=grid) - elif aac.is_jax_namespace(xp): - return self._call_jax(x, y) - else: - raise NotImplementedError( - f"BoundedRectBivariateSpline not implemented for {xp.__name__} backend" - ) - - def _call_scipy(self, x, y, dx=0, dy=0, grid=False): - result = super().__call__(x=x, y=y, dx=dx, dy=dy, grid=grid) - out_of_bounds_x = (x < self.x_min) | (x > self.x_max) - out_of_bounds_y = (y < self.y_min) | (y > self.y_max) - bad = out_of_bounds_x | out_of_bounds_y - result[bad] = self.fill_value - if result.size == 1: - if bad: - return self.fill_value - else: - return result.item() - else: - return result - - def _call_jax(self, x, y): - import jax.numpy as jnp - from interpax import interp2d - - return interp2d( - jnp.asarray(x), - jnp.asarray(y), - jnp.asarray(self.x), - jnp.asarray(self.y), - jnp.asarray(self.z), - extrap=self.fill_value if self.fill_value is not None else False, - method="cubic2", - ) - - class WrappedInterp1d(interp1d): """ A wrapper around scipy interp1d which sets equality-by-instantiation and diff --git a/bilby/gw/likelihood/base.py b/bilby/gw/likelihood/base.py index 0030716ae..2e86a95bf 100644 --- a/bilby/gw/likelihood/base.py +++ b/bilby/gw/likelihood/base.py @@ -1,5 +1,4 @@ -import os import copy import array_api_compat as aac @@ -8,11 +7,12 @@ from scipy.special import logsumexp from ...core.likelihood import Likelihood -from ...core.utils import logger, BoundedRectBivariateSpline, create_time_series +from ...core.utils import logger, create_time_series 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 ...compat.utils import array_module class GravitationalWaveTransient(Likelihood): @@ -57,13 +57,11 @@ class GravitationalWaveTransient(Likelihood): Warning: when using marginalisation the dict is overwritten which will change the the dict you are passing in. If this behaviour is undesired, pass `priors.copy()`. distance_marginalization_lookup_table: (dict, str), optional - If a dict, dictionary containing the lookup_table, distance_array, - (distance) prior_array, and reference_distance used to construct - the table. - If a string the name of a file containing these quantities. - The lookup table is stored after construction in either the - provided string or a default location: - '.distance_marginalization_lookup_dmin{}_dmax{}_n{}.npz' + Deprecated and will be removed in bilby v3.1.0: distance + marginalization is evaluated with :code:`distmarg` and no longer uses + a lookup table. If a string, it names the file to which a small + record of the tabulated distance prior is written (default + '.distance_marginalization_lookup.npz'); a dict is ignored. calibration_lookup_table: dict, optional If a dict, contains the arrays over which to marginalize for each interferometer or the filepaths of the calibration files. @@ -207,13 +205,7 @@ def __init__( if self.distance_marginalization: self._lookup_table_filename = None self._check_marginalized_prior_is_set(key='luminosity_distance') - self._distance_array = np.linspace( - self.priors['luminosity_distance'].minimum, - self.priors['luminosity_distance'].maximum, int(1e4)) - self.distance_prior_array = np.array( - [self.priors['luminosity_distance'].prob(distance) - for distance in self._distance_array]) - self._ref_dist = self.priors['luminosity_distance'].rescale(0.5) + self._ref_dist = float(self.priors['luminosity_distance'].rescale(0.5)) self._setup_distance_marginalization( distance_marginalization_lookup_table) for key in ['redshift', 'comoving_distance']: @@ -684,25 +676,17 @@ def generate_distance_sample_from_marginalized_likelihood( d_inner_h, h_inner_h = self._calculate_inner_products( signal_polarizations, parameters=parameters ) - - d_inner_h_dist = ( - d_inner_h * parameters['luminosity_distance'] / self._distance_array - ) - - h_inner_h_dist = ( - h_inner_h * parameters['luminosity_distance']**2 / self._distance_array**2 - ) - - if self.phase_marginalization: - distance_log_like = ln_i0(abs(d_inner_h_dist)) - h_inner_h_dist.real / 2 + rho_mf, rho_opt = self._reference_snrs( + d_inner_h, h_inner_h, parameters=parameters) + rho_mf = float(np.asarray(rho_mf)) + rho_opt = float(np.asarray(rho_opt)) + if rho_opt > 0: + from ...core.utils import random + new_distance = float(self._distance_marginalizer.sample_distance( + rho_mf, rho_opt, self._ref_dist, rng=random.rng)) else: - distance_log_like = (d_inner_h_dist.real - h_inner_h_dist.real / 2) - - distance_post = (np.exp(distance_log_like - max(distance_log_like)) * - self.distance_prior_array) - - new_distance = Interped( - self._distance_array, distance_post).sample() + # zero template: the conditional posterior is the prior + new_distance = self.priors['luminosity_distance'].sample() self._rescale_signal(signal_polarizations, new_distance) return new_distance @@ -773,15 +757,73 @@ def generate_phase_sample_from_marginalized_likelihood( return new_phase def distance_marginalized_likelihood(self, d_inner_h, h_inner_h, *, parameters): + """ + Log likelihood ratio marginalized over luminosity distance. + + Evaluated with :code:`distmarg` from the matched-filter and optimal + SNRs at the reference distance; the result follows the array + namespace of the inputs (numpy, JAX, ...). + + Parameters + ========== + d_inner_h: complex or array-like + :math:`\\langle d | h \\rangle` at the current luminosity distance, + possibly with time-bin and/or calibration axes. + h_inner_h: float or array-like + :math:`\\langle h | h \\rangle` at the current luminosity distance. + parameters: dict + The current parameters. + + Returns + ======= + array-like + :math:`\\ln(L_D / Z_N)` with the shape of :code:`d_inner_h`; + zero where the template vanishes. + """ + rho_mf, rho_opt = self._reference_snrs( + d_inner_h, h_inner_h, parameters=parameters) + xp = array_module(rho_mf) + valid = rho_opt > 0 + ln_l = self._distance_marginalizer.ln_likelihood_ratio( + rho_mf, xp.where(valid, rho_opt, 1.0), self._ref_dist) + return xp.where(valid, ln_l, 0.0) + + def _reference_snrs(self, d_inner_h, h_inner_h, *, parameters): + """ + Convert inner products to the matched-filter and optimal SNRs at the + reference distance, as used by :code:`distmarg`. + + Parameters + ========== + d_inner_h: complex or array-like + :math:`\\langle d | h \\rangle` at the current luminosity distance. + h_inner_h: float or array-like + :math:`\\langle h | h \\rangle` at the current luminosity distance. + parameters: dict + The current parameters. + + Returns + ======= + rho_mf, rho_opt: array-like + Matched-filter SNR (real part, or modulus with phase + marginalization) and optimal SNR at :code:`self._ref_dist`; + :code:`rho_opt` gains trailing axes to broadcast against + :code:`rho_mf`. + """ d_inner_h_ref, h_inner_h_ref = self._setup_rho( d_inner_h, h_inner_h, parameters=parameters) + xp = array_module(d_inner_h_ref) if self.phase_marginalization: d_inner_h_ref = abs(d_inner_h_ref) else: d_inner_h_ref = d_inner_h_ref.real - - return self._interp_dist_margd_loglikelihood( - d_inner_h_ref, h_inner_h_ref) + d_inner_h_ref = xp.asarray(d_inner_h_ref) + rho_opt = xp.sqrt(xp.asarray(h_inner_h_ref)) + extra = d_inner_h_ref.ndim - rho_opt.ndim + if rho_opt.ndim > 0 and extra > 0: + rho_opt = xp.reshape(rho_opt, rho_opt.shape + (1,) * extra) + rho_mf = d_inner_h_ref / xp.where(rho_opt > 0, rho_opt, 1.0) + return rho_mf, rho_opt def phase_marginalized_likelihood(self, d_inner_h, h_inner_h): d_inner_h = ln_i0(abs(d_inner_h)) @@ -887,51 +929,57 @@ def _setup_rho(self, d_inner_h, optimal_snr_squared, parameters): def log_likelihood(self, parameters): return self.log_likelihood_ratio(parameters=parameters) + self.noise_log_likelihood() - @property - def _delta_distance(self): - return self._distance_array[1] - self._distance_array[0] - - @property - def _dist_multiplier(self): - ''' Maximum value of ref_dist/dist_array ''' - return self._ref_dist / self._distance_array[0] + def _setup_distance_marginalization(self, lookup_table=None): + """ + Set up distance marginalization with :code:`distmarg`. - @property - def _optimal_snr_squared_ref_array(self): - """ Optimal filter snr at fiducial distance of ref_dist Mpc """ - return np.logspace(-5, 10, self._dist_margd_loglikelihood_array.shape[0]) + The luminosity-distance prior is tabulated (for + :code:`bilby.gw.prior.Cosmological` priors from the prior's own + cosmology, not its interpolated :code:`prob`) and fitted once; no + lookup table is built. - @property - def _d_inner_h_ref_array(self): - """ Matched filter snr at fiducial distance of ref_dist Mpc """ - if self.phase_marginalization: - return np.logspace(-5, 10, self._dist_margd_loglikelihood_array.shape[1]) - else: - n_negative = self._dist_margd_loglikelihood_array.shape[1] // 2 - n_positive = self._dist_margd_loglikelihood_array.shape[1] - n_negative - return np.hstack(( - -np.logspace(3, -3, n_negative), np.logspace(-3, 10, n_positive) - )) - - def _setup_distance_marginalization(self, lookup_table=None): + Parameters + ========== + lookup_table: str, dict, optional + Deprecated, will be removed in bilby v3.1.0. A string sets the + file to which the tabulated prior is recorded (see + :code:`cache_lookup_table`); a dict, e.g. a table built by an + older version, is ignored. + """ + try: + from distmarg import DistanceMarginalization + from distmarg.bilby import prior_to_arrays + from scipy.integrate import trapezoid + except ImportError as e: + raise ImportError( + "Distance marginalization requires the distmarg package, " + "install it with `pip install distmarg`." + ) from e + if lookup_table is not None: + import warnings + + warnings.warn( + "The distance_marginalization_lookup_table argument is " + "deprecated and will be removed in bilby v3.1.0: distance " + "marginalization no longer uses a lookup table.", + DeprecationWarning, + stacklevel=3, + ) if isinstance(lookup_table, str) or lookup_table is None: self.cached_lookup_table_filename = lookup_table - lookup_table = self.load_lookup_table( - self.cached_lookup_table_filename) - if isinstance(lookup_table, dict): - if self._test_cached_lookup_table(lookup_table): - self._dist_margd_loglikelihood_array = lookup_table[ - 'lookup_table'] - else: - self._create_lookup_table() - else: - self._create_lookup_table() - self._interp_dist_margd_loglikelihood = BoundedRectBivariateSpline( - self._d_inner_h_ref_array, - self._optimal_snr_squared_ref_array, - self._dist_margd_loglikelihood_array.T, - fill_value=-np.inf, + + prior = self.priors['luminosity_distance'] + distance, ln_prior = prior_to_arrays(prior) + self._distance_marginalizer = DistanceMarginalization( + distance, ln_prior, self.phase_marginalization) + self._distance_array = distance + density = np.exp(ln_prior - np.max(ln_prior)) + self.distance_prior_array = density / trapezoid(density, distance) + logger.info( + f"Distance marginalization with distmarg ({self._distance_marginalizer.backend} backend), " + f"reference distance {self._ref_dist:.1f} Mpc" ) + self.cache_lookup_table() @property def cached_lookup_table_filename(self): @@ -947,76 +995,21 @@ def cached_lookup_table_filename(self, filename): filename += '.npz' self._lookup_table_filename = filename - def load_lookup_table(self, filename): - if os.path.exists(filename): - try: - loaded_file = dict(np.load(filename)) - except AttributeError as e: - logger.warning(e) - self._create_lookup_table() - return None - match, failure = self._test_cached_lookup_table(loaded_file) - if match: - logger.info('Loaded distance marginalisation lookup table from ' - '{}.'.format(filename)) - return loaded_file - else: - logger.info('Loaded distance marginalisation lookup table does ' - 'not match for {}.'.format(failure)) - elif isinstance(filename, str): - logger.info('Distance marginalisation file {} does not ' - 'exist'.format(filename)) - return None - def cache_lookup_table(self): + """ + Record the tabulated distance prior used for marginalization. + + There is no lookup table any more; this small file (a few tens of kB) + keeps workflows that read :code:`cached_lookup_table_filename`, such as + :code:`bilby_pipe`, working. Deprecated together with the lookup-table + argument and will be removed in bilby v3.1.0. + """ np.savez(self.cached_lookup_table_filename, distance_array=self._distance_array, prior_array=self.distance_prior_array, - lookup_table=self._dist_margd_loglikelihood_array, reference_distance=self._ref_dist, phase_marginalization=self.phase_marginalization) - def _test_cached_lookup_table(self, loaded_file): - pairs = dict( - distance_array=self._distance_array, - prior_array=self.distance_prior_array, - reference_distance=self._ref_dist, - phase_marginalization=self.phase_marginalization) - for key in pairs: - if key not in loaded_file: - return False, key - elif not np.allclose(np.atleast_1d(loaded_file[key]), - np.atleast_1d(pairs[key]), - rtol=1e-15): - return False, key - return True, None - - def _create_lookup_table(self): - """ Make the lookup table """ - from tqdm.auto import tqdm - logger.info('Building lookup table for distance marginalisation.') - - self._dist_margd_loglikelihood_array = np.zeros((400, 800)) - scaling = self._ref_dist / self._distance_array - d_inner_h_array_full = np.outer(self._d_inner_h_ref_array, scaling) - h_inner_h_array_full = np.outer(self._optimal_snr_squared_ref_array, scaling ** 2) - if self.phase_marginalization: - d_inner_h_array_full = ln_i0(abs(d_inner_h_array_full)) - prior_term = self.distance_prior_array * self._delta_distance - for ii, optimal_snr_squared_array in tqdm( - enumerate(h_inner_h_array_full), total=len(self._optimal_snr_squared_ref_array) - ): - for jj, d_inner_h_array in enumerate(d_inner_h_array_full): - self._dist_margd_loglikelihood_array[ii][jj] = logsumexp( - d_inner_h_array - optimal_snr_squared_array / 2, - b=prior_term - ) - log_norm = logsumexp( - 0 / self._distance_array, b=self.distance_prior_array * self._delta_distance - ) - self._dist_margd_loglikelihood_array -= log_norm - self.cache_lookup_table() - def _setup_phase_marginalization(self, min_bound=-5, max_bound=10): logger.warning( "The _setup_phase_marginalization method is deprecated and will be removed, " diff --git a/gw_requirements.txt b/gw_requirements.txt index dded1efca..7c5bc194a 100644 --- a/gw_requirements.txt +++ b/gw_requirements.txt @@ -3,3 +3,4 @@ lalsuite gwpy pyfftw scikit-learn +distmarg[rust]>=0.1.0 diff --git a/pixi.lock b/pixi.lock index 451213db0..cf906ccd2 100644 --- a/pixi.lock +++ b/pixi.lock @@ -866,9 +866,11 @@ environments: - pypi: ./ - pypi: https://files.pythonhosted.org/packages/09/dc/f3dfb7488b770f3f67e6545085bf2abea5172e88f57b8ad25ef860ca704c/myst_parser-5.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/31/ce/6c0dec3f7cc49efe66cbc611160496703e55961b1dd36fbfb558d300c558/nessai_bilby-0.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/34/c8/ea1a1084e0e6d92915bbe46da30940da4e231b3af440f348f7e144384b7c/nessai-0.15.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/36/ac/4ee28c65861605945223145fbcd3c9362ec2255ddf7d917574e205548c82/jaxlib-0.10.2-cp313-cp313-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/40/ff/062a65c322e0dd4225a78627243b2925ebc902d5a6282871506d1d6767fd/bilby_cython-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/48/cb/99ee8021fa305d65b1fb369fabf96ccc65c60282407390e5355bb9e62f9f/zeus_mcmc-2.5.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/49/a6/cf084d85b8ce2b912337768d1181acf02a63264d18af674df1c84991392b/zeus_bilby-0.1.0-py3-none-any.whl @@ -1323,9 +1325,11 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/09/dc/f3dfb7488b770f3f67e6545085bf2abea5172e88f57b8ad25ef860ca704c/myst_parser-5.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/31/ce/6c0dec3f7cc49efe66cbc611160496703e55961b1dd36fbfb558d300c558/nessai_bilby-0.3.1-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/34/c8/ea1a1084e0e6d92915bbe46da30940da4e231b3af440f348f7e144384b7c/nessai-0.15.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/48/cb/99ee8021fa305d65b1fb369fabf96ccc65c60282407390e5355bb9e62f9f/zeus_mcmc-2.5.4-py3-none-any.whl @@ -1788,7 +1792,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./ - pypi: https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/36/ac/4ee28c65861605945223145fbcd3c9362ec2255ddf7d917574e205548c82/jaxlib-0.10.2-cp313-cp313-manylinux_2_27_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/40/ff/062a65c322e0dd4225a78627243b2925ebc902d5a6282871506d1d6767fd/bilby_cython-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/80/0c/2ed47112fc1958a0a81c9b015d4e1861953a1ec3a17b081c0180a25ce82c/lineax-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/89/82/5ab5211079a151b6f661529369c0c8e98ec64cabf5c0cf22a0a05af124d8/jax-0.10.2-py3-none-any.whl @@ -2169,7 +2175,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5f/60/3a384798c7df2462343028d37e6adb9079eb0b155a50736b88bdd668d825/bilby_cython-0.5.4.tar.gz - pypi: https://files.pythonhosted.org/packages/80/0c/2ed47112fc1958a0a81c9b015d4e1861953a1ec3a17b081c0180a25ce82c/lineax-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/89/82/5ab5211079a151b6f661529369c0c8e98ec64cabf5c0cf22a0a05af124d8/jax-0.10.2-py3-none-any.whl @@ -2620,6 +2628,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./ - pypi: git+https://github.com/joshspeagle/dynesty.git?branch=master#d8affbcd18d1cb894e0c7102ba31c65794461b55 + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/40/ff/062a65c322e0dd4225a78627243b2925ebc902d5a6282871506d1d6767fd/bilby_cython-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda @@ -2976,6 +2986,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ./ - pypi: git+https://github.com/joshspeagle/dynesty.git?branch=master#d8affbcd18d1cb894e0c7102ba31c65794461b55 + - pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5f/60/3a384798c7df2462343028d37e6adb9079eb0b155a50736b88bdd668d825/bilby_cython-0.5.4.tar.gz numpy: channels: @@ -3415,6 +3427,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/40/ff/062a65c322e0dd4225a78627243b2925ebc902d5a6282871506d1d6767fd/bilby_cython-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl osx-arm64: @@ -3788,6 +3802,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5f/60/3a384798c7df2462343028d37e6adb9079eb0b155a50736b88bdd668d825/bilby_cython-0.5.4.tar.gz - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl py311: @@ -4229,6 +4245,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.9.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f7/99/4b8ed531febe5aca745b5522d19d5d2e18744e4cb0d9cd83c0eaad464fd5/bilby_cython-0.5.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl osx-arm64: @@ -4604,6 +4622,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5f/60/3a384798c7df2462343028d37e6adb9079eb0b155a50736b88bdd668d825/bilby_cython-0.5.4.tar.gz - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl py312: @@ -5044,7 +5064,9 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/0a/1584b9f60741fd045b9fa57576a524256f31ac9e00162b2c5355f7ef2c80/bilby_cython-0.5.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_3.conda @@ -5426,6 +5448,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/2d/32/960859bf3552f3d0e303924d1ce8b02470c6dbf683d985f7377fde41681c/bilby_cython-0.5.4-cp312-cp312-macosx_12_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl py313: @@ -5866,6 +5890,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/40/ff/062a65c322e0dd4225a78627243b2925ebc902d5a6282871506d1d6767fd/bilby_cython-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl osx-arm64: @@ -6222,6 +6248,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5f/60/3a384798c7df2462343028d37e6adb9079eb0b155a50736b88bdd668d825/bilby_cython-0.5.4.tar.gz - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl py313-base: @@ -6961,6 +6989,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/xarray-einstats-0.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl + - pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/40/ff/062a65c322e0dd4225a78627243b2925ebc902d5a6282871506d1d6767fd/bilby_cython-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl osx-arm64: @@ -7334,6 +7364,8 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: ./ + - pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/5f/60/3a384798c7df2462343028d37e6adb9079eb0b155a50736b88bdd668d825/bilby_cython-0.5.4.tar.gz - pypi: https://files.pythonhosted.org/packages/f1/f9/41c72d0b5fb511fdf23cf62ed50da6c73d4497f03ed1cad04f214dc477c8/dynesty-3.1.0-py3-none-any.whl packages: @@ -27745,6 +27777,7 @@ packages: - gwpy ; extra == 'all' - pyfftw ; extra == 'all' - scikit-learn ; extra == 'all' + - distmarg[rust]>=0.1.0 ; extra == 'all' - interpax ; extra == 'all' - jax ; extra == 'all' - orng>=0.1 ; extra == 'all' @@ -27761,6 +27794,7 @@ packages: - gwpy ; extra == 'gw' - pyfftw ; extra == 'gw' - scikit-learn ; extra == 'gw' + - distmarg[rust]>=0.1.0 ; extra == 'gw' - interpax ; extra == 'jax' - jax ; extra == 'jax' - orng>=0.1 ; extra == 'jax' @@ -27788,6 +27822,13 @@ packages: - sphinx-rtd-theme ; extra == 'dev' - numpydoc ; extra == 'dev' requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/00/fb/129607621b03fa3f0435275eb4741407e8bda7f94fc5e17bc73b16f5c5df/distmarg_rs-0.1.0-cp310-abi3-macosx_11_0_arm64.whl + name: distmarg-rs + version: 0.1.0 + sha256: 6615b9b263d6136b0db79cc7afa78fb98d0d500891a7fcb41aa7600b2e627058 + requires_dist: + - numpy>=1.24 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/05/7f/798705f5296a58ca505d600456748d1be48078eac8a7050d8a98bc9edb89/decorator-5.3.1-py3-none-any.whl name: decorator version: 5.3.1 @@ -27835,6 +27876,22 @@ packages: version: 3.4.0 sha256: 69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd requires_python: '>=3.8' +- pypi: https://files.pythonhosted.org/packages/28/f8/f73cf661fdb42b1fdb6186eeee9a7ede0af0e9259d96b260219a858adeb8/distmarg-0.1.0-py3-none-any.whl + name: distmarg + version: 0.1.0 + sha256: baabd10f9464be6b76348a9fb1ba7ee87550855047583402a40b5042a10bf303 + requires_dist: + - numpy>=1.24 + - scipy>=1.15 + - array-api-compat>=1.4 + - distmarg-rs==0.1.0 ; extra == 'rust' + - jax>=0.4 ; extra == 'jax' + - bilby ; extra == 'bilby' + - astropy ; extra == 'bilby' + - pytest ; extra == 'test' + - scipy ; extra == 'test' + - mpmath ; extra == 'test' + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/2d/0a/1584b9f60741fd045b9fa57576a524256f31ac9e00162b2c5355f7ef2c80/bilby_cython-0.5.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl name: bilby-cython version: 0.5.4 @@ -27905,6 +27962,13 @@ packages: - numpy>=2.0 - ml-dtypes>=0.5.0 requires_python: '>=3.11' +- pypi: https://files.pythonhosted.org/packages/3c/14/463e16d46d8c71a05744dab690240e6a34a1d60a91d580fec7604963dc66/distmarg_rs-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + name: distmarg-rs + version: 0.1.0 + sha256: 2e7f2bcabe9747b2007e1a24fcdc3bf8328150fbac034752534754e4b7fef8cf + requires_dist: + - numpy>=1.24 + requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/40/ff/062a65c322e0dd4225a78627243b2925ebc902d5a6282871506d1d6767fd/bilby_cython-0.5.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl name: bilby-cython version: 0.5.4 diff --git a/pixi.toml b/pixi.toml index 4234b3d85..848aeaa27 100644 --- a/pixi.toml +++ b/pixi.toml @@ -63,6 +63,7 @@ python-lalsimulation = "*" [feature.gw.pypi-dependencies] bilby-cython = ">=0.5.4" +distmarg = { version = ">=0.1.0", extras = ["rust"] } [feature.jax.dependencies] numpy = "<2.5" diff --git a/test/core/utils_test.py b/test/core/utils_test.py index aee0c5437..bb83d92b2 100644 --- a/test/core/utils_test.py +++ b/test/core/utils_test.py @@ -381,49 +381,6 @@ def plot(): self.assertTrue(os.path.isfile(self.filename)) -@pytest.mark.array_backend -@pytest.mark.usefixtures("xp_class") -class TestUnsortedInterp2d(unittest.TestCase): - def setUp(self): - if aac.is_torch_namespace(self.xp): - pytest.skip("Skipping Interp2d tests for torch backend") - self.xx = np.linspace(0, 1, 10) - self.yy = np.linspace(0, 1, 10) - self.zz = np.random.random((10, 10)) - self.interpolant = bilby.core.utils.BoundedRectBivariateSpline(self.xx, self.yy, self.zz) - - def tearDown(self): - pass - - def test_returns_float_for_floats(self): - self.assertIsInstance(self.interpolant(0.5, 0.5), float) - - def test_returns_none_for_floats_outside_range(self): - self.assertIsNone(self.interpolant(0.5, -0.5)) - self.assertIsNone(self.interpolant(-0.5, 0.5)) - - def test_returns_float_for_float_and_array(self): - input_array = self.xp.asarray(np.random.random(10)) - self.assertEqual(aac.get_namespace(self.interpolant(input_array, 0.5)), self.xp) - self.assertEqual(aac.get_namespace( - self.interpolant(input_array, input_array)), self.xp - ) - self.assertEqual(aac.get_namespace(self.interpolant(0.5, input_array)), self.xp) - - def test_raises_for_mismatched_arrays(self): - with self.assertRaises(ValueError): - self.interpolant( - self.xp.asarray(np.random.random(10)), - self.xp.asarray(np.random.random(20)), - ) - - def test_returns_fill_in_correct_place(self): - x_data = self.xp.asarray(np.random.random(10)) - y_data = self.xp.asarray(np.random.random(10)) - x_data = xpx.at(x_data, 3).set(-1) - self.assertTrue(self.xp.isnan(self.interpolant(x_data, y_data)[3])) - - @pytest.mark.array_backend @pytest.mark.usefixtures("xp_class") class TestTrapeziumRuleIntegration(unittest.TestCase):